How can a Caliburn.Micro sub menuitem click call an action on the containing view's viewmodel? - menuitem

I have a top level menu in my ShellView and when selecting a sub MenuItem, I would like to call the following method on the ShellViewModel (a Conductor.Collection.AllActive).
public void SelectServer(string pServerName)
{
mDefaultClaimServer = pServerName;
}
The following does not work as no method gets called (I have tried various signatures and action parameters) -
<Menu Name="menu1" DockPanel.Dock="Top">
<MenuItem Header="Select Server" Name="ClaimServers">
<MenuItem.ItemTemplate>
<DataTemplate>
<!-- we need this else we show the class name -->
<TextBlock Text="{Binding DisplayName}">
<ContentControl cal:Message.Attach="[Event Click] = [Action TxTester.ShellViewModel.SelectServer($Text)]"/>
</TextBlock>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</Menu>
The following does call the ShellViewModel SelectServer method but I get null for the text of the clicked sub MenuItem (I also tried many other signatures and action parameters) -
<Menu Name="menu1" DockPanel.Dock="Top">
<MenuItem Header="Select Server" Name="ClaimServers" cal:Message.Attach="SelectServer($this.Text)">
<MenuItem.ItemTemplate>
<DataTemplate>
<!-- we need this else we show the class name -->
<TextBlock Text="{Binding DisplayName}" />
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</Menu>
I've been struggling with this a long time and can't figure it out. Can someone suggest the proper combination where I can pass the header text of a sub MenuItem to the ShellViewModel SelectServer method?

I got what I was trying to do working, per a post from Rob Eisenberg describing a "special trick to get the text from bound submenus" here - http://caliburnmicro.codeplex.com/discussions/287228
I would still love to know how to do what I was trying to do with standard OOTB logic if anyone has suggestions, so that I am able to understand CM better.
Basically I added this to the bootstrapper Configure() overide -
MessageBinder.SpecialValues.Add("$originalsourcecontext", context =>
{
var args = context.EventArgs as RoutedEventArgs;
if (args == null)
return null;
var fe = args.OriginalSource as FrameworkElement;
if (fe == null)
return null;
return fe.DataContext;
});
and added this to the xaml -
<MenuItem Header="_Select Server" Name="ClaimServers" cal:Message.Attach="SelectServer($originalsourcecontext)" />
and then I was passed the header text of the sub menuitem which is what I wanted.

Related

How can I get an element from within a ListViewItem's ItemTemplate?

I have a ListView that uses a custom ItemTemplate (doesn't everyone?):
<ListView>
<!-- ... -->
<ListView.ItemTemplate>
<DataTemplate x:DataType="x:String">
<MyGreatControl Thing="{x:Bind}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
MyGreatControl today has extensive keyboard handling code built-in, but due to some refactoring, I need to move the actual handlers to the ListView itself. However, I don't want to move all of the code in MyGreatControl to the ListView (for many reasons).
If I have an arbitrary ListViewItem (which, for example, I can get from an event handler), how can I access the MyGreatControl instance in its DataTemplate?
MyGreatControl^ GetMyGreatControlFromListViewItem(ListViewItem^ listViewItem) {
// ???
}
Disclaimer: I work for Microsoft.
You want to use ContentTemplateRoot!
MyGreatControl^ GetMyGreatControlFromListViewItem(ListViewItem^ listViewItem) {
return safe_cast<MyGreatControl^>(listViewItem->ContentTemplateRoot);
}
This also works for any arbitrary element—if you have a StackPanel, for example, ContentTemplateRoot will return the StackPanel instance you want:
<ListView.ItemTemplate>
<DataTemplate x:DataType="x:String">
<StackPanel><!-- This is what you get! -->
<TextBlock Text="{x:Bind}" />
<Button Content="Foo" IsTabStop="False" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
You can then use normal visual tree navigation to find Children, etc.

UWP: Set text to bold in TreeNode

I am following Microsoft's documentation to implement a TreeView in a Universal Windows Platform app in C++. I have successfully been able to create a tree view with one node using the following codes:
XAML:
<TreeView x:Name="treeSolution"></TreeView>
C++:
TreeViewNode ^treeNode = ref new TreeViewNode();
treeNode->Content = "Hello";
treeSolution->RootNodes->Append(treeNode);
Now, I want to set the text to bold. I tried the following:
TextBlock ^textBlock = ref new TextBlock();
textBlock->Text = "Hello";
textBlock->FontWeight = Windows::UI::Text::FontWeights::Bold;
treeNode->Content = textBlock;
treeSolution->RootNodes->Append(treeNode);
The code displays Windows.UI.Xaml.Controls.TextBlock instead of Hello in bold.
The documentation says that In Windows 10, version 1803, you have to retemplate the TreeView control and specify a custom ItemTemplate if your content is not a string. It then gives a complex example using the Music and Picture library.
Could somebody provide a simple example of how to display the text in bold? Thanks.
You have to provide a custom style for the whole control in XAML to be able to set the TreeViewItemDataTemplate:
<DataTemplate x:Key="TreeViewItemDataTemplate">
<Grid Height="44">
<TextBlock
Text="{Binding Content}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Style="{ThemeResource BodyTextBlockStyle}"
FontWeight="Bold" />
</Grid>
</DataTemplate>
<Style TargetType="TreeView">
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeView">
<TreeViewList x:Name="ListControl"
ItemTemplate="{StaticResource TreeViewItemDataTemplate}"
ItemContainerStyle="{StaticResource TreeViewItemStyle}"
CanDragItems="True"
AllowDrop="True"
CanReorderItems="True">
<TreeViewList.ItemContainerTransitions>
<TransitionCollection>
<ContentThemeTransition />
<ReorderThemeTransition />
<EntranceThemeTransition IsStaggeringEnabled="False" />
</TransitionCollection>
</TreeViewList.ItemContainerTransitions>
</TreeViewList>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Problems when using DataTemplate in a UWP app (crashing, data not being set)

I'm getting my feet wet trying out Windows 10 UWP app development. I've installed Visual Studio 2015 and am currently playing around with trying to figure out how to work with data binding.
The following is my simple XAML:
<Grid>
<Pivot x:Name="docPivot"
ItemsSource="{Binding}">
<Pivot.ItemTemplate>
<DataTemplate>
<PivotItem Header="{Binding Filename}">
<TextBox Text="{Binding Contents}"/>
</PivotItem>
</DataTemplate>
</Pivot.ItemTemplate>
</Pivot>
</Grid>
This is my Mainpage.xaml.cpp in relevant part: (Document is a simple struct that just has two properties, a String Filename and a String Contents.)
MainPage::MainPage()
{
InitializeComponent();
auto docs = ref new Vector<Document^>();
auto doc1 = ref new Document();
doc1->Filename = "Filename1";
doc1->Contents = "Contents 1";
docs->Append(doc1);
auto doc2 = ref new Document();
doc2->Filename = "Filename2";
doc2->Contents = "Contents 2";
docs->Append(doc2);
docPivot->ItemsSource = docs;
}
However, I'm having a pair of issues I can't figure out:
The first is, instead of each PivotItem's header being Filename, they're both MyApp.Document, where MyApp is my namespace.
The second issue is, the TextBox is being properly populated with the contents from the data binding, and the two PivotItems can be switched between, but as soon as I try and select a Textbox, the app crashes with an access violation:
Exception thrown at 0x0004CE1E in MyApp.exe: 0xC0000005: Access violation reading location 0x00000000.
Any input on what I'm doing wrong here?
First you must add Bindable attribute to Document class.
[Windows::UI::Xaml::Data::Bindable]
public ref class Document sealed
And you must add
#include "Document.h"
in Mainpage.xaml.h file not the .cpp file. You Pivot's ItemTemplate should not contain PivotItem, you should do like this
<Grid>
<Pivot x:Name="docPivot">
<Pivot.HeaderTemplate>
<DataTemplate>
<ContentControl Content="{Binding Filename}"/>
</DataTemplate>
</Pivot.HeaderTemplate>
<Pivot.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Contents}"/>
</DataTemplate>
</Pivot.ItemTemplate>
</Pivot>

Infragistics web datagrid header checkbox Event issue

ISSUE
I am new to infragistics, I am using the webdatagrid in my page. My grid has a check box as one of the fields. The header field is also a check box .
In the server side event( i need this in the server event no client side events:) ) of the header check box . I need to bind the grid .
If header check box is checked i need to display only active records and if its unchecked i need to display all records .
I am implementing this logic in the server function of the header check box and the grid is not binding( ie the values are not changed) in this event .
I am working on this fix for over a week now . Please help
Please see the code below ...........
ASPX
<ig:WebDataGrid ID="WebDataGrid1" runat="server" EnableDataViewState="true" ViewStateMode="Enabled" Height="100%" Width="100%" AutoGenerateColumns="False" OnColumnSorted="WebDataGrid1_ColumnSorted" DataKeyFields="Id">
<Columns>
<ig:BoundDataField DataFieldName="Id" Hidden="True" Key="Id">
<Header Text="BoundColumn_0" />
</ig:BoundDataField>
<ig:BoundDataField DataFieldName="Name" Key="Name">
<Header Text="Name"></Header>
</ig:BoundDataField>
<ig:BoundDataField DataFieldName="ShortName" Key="ShortName">
<Header Text="ShortName"></Header>
</ig:BoundDataField>
<ig:BoundDataField DataFieldName="IsoCode" Key="IsoCode">
<Header Text="IsoCode"></Header>
</ig:BoundDataField>
<ig:TemplateDataField Key="IsActive" Header-Text="IsActive">
<HeaderTemplate>
<asp:CheckBox ID="chkChildIsActive" runat="server" Checked="true"
Text="IsActive" AutoPostBack="True"
oncheckedchanged="chkChildIsActive_CheckedChanged">
</asp:CheckBox>
</HeaderTemplate>
<ItemTemplate>
<asp:CheckBox ID="chkHIsActive" runat="server" Checked='<%# DataBinder.Eval(((Infragistics.Web.UI.TemplateContainer)Container).DataItem, "IsActive") %>'>
</asp:CheckBox>
</ItemTemplate>
<Header Text="IsActive" />
</ig:TemplateDataField>
</Columns>
<ClientEvents Click="WebDataGrid1_SingleClick" DoubleClick="WebDataGrid1_DoubleClick" />
<Behaviors>
<ig:Paging PageSize="15" QuickPages="9">
<PagerTemplate>
<uc1:CustomerPagerControl ID="CustomerPager" runat="server" />
</PagerTemplate>
</ig:Paging>
<ig:Sorting>
</ig:Sorting>
<ig:RowSelectors>
</ig:RowSelectors>
<ig:Selection RowSelectType="Single" CellClickAction="Row" CellSelectType="None">
</ig:Selection>
</Behaviors>
</ig:WebDataGrid>
ASPX.cs
// Page load binding data to grid
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
List<Port> port = PortManager.PortListTestPaging(true);
WebDataGrid1.DataSource = port;
WebDataGrid1.DataBind();
}
}
//Checkbox Event
protected void chkChildIsActive_CheckedChanged(object sender, EventArgs e)
{
CheckBox chkChildIsActive = (CheckBox)sender;
List<Port> port = PortManager.PortListTestPaging(chkChildIsActive.Checked);
WebDataGrid1.DataSource = port;
WebDataGrid1.DataBind();
}
Fixes that i tried from the Infragistics forum:
EnableDataViewState = false : If we give this property to grid then on the checkbox event the grids all data is cleared ( and the check box event is not fired) .
EnableDataViewState = true and WebDataGrid1.ClearDataSource(): Then I had the following error occurred on check box event "Multiple controls with the same ID 'xyz0_4' were found" I have gone through the below link for a fix but failed .
http://www.infragistics.com/community/forums/p/65065/329361.aspx
WebDataGrid1.Rows.Clear() & //WebDataGrid1.Columns.Clear() : Both not worked .
I wanted to know what the issue was so itried the same code for a button event with button outside the grid and it worked . So i think its some problem with some of the properties in the webdatagrid that i may have wrongly given . I have gone through all possible work around that were available .
Thanks ,
Sankardeep V
The simplest solution is to move your logic rebinding the grid from your CheckChanged event handler to the Page_PreRender event. To do this you will need to store if the CheckChanged event was fired and what the value of the check box is in private variables to the page and then check them in the PreRender event. This should work the same as it did in a Button click for you.

how to make a xaml control with different view modes

I'm trying to learn how to separate a view from its associated viewmodel, while making the view have as little or no code-behind as possible.
my control has a textblock when the object is in a display mode, and a textbox when the user wants to edit that field. In both cases, these controls must bind to the same string in the modelview, but only the appropriate one should be displayed depending on the state of the viewmodel. Previously, I'd just modify the panels child to a new element in the codebehind... but as I understand it, I should be trying to make all my changes in XAML for the view.
My viewmodel has a bool denoting if its in display or edit mode. Is there a way to specify use of a different template depending on the value of that bool, but keep it all in XAML?
There is a way to do what you say you're working on, by using DataTriggers.
First, define a Style which contains the DataTriggers you want to use. For example, note here two identical styles for a ContentControl, each with a DataTrigger pair that performs the opposite of the other:
<Window.Resources>
<Style TargetType="{x:Type ContentControl}" x:Key="HiddenWhenFalse" >
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Value="False" Binding="{Binding MyBooleanValue}">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Value="True" Binding="{Binding MyBooleanValue}">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type ContentControl}" x:Key="HiddenWhenTrue" >
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Value="True" Binding="{Binding MyBooleanValue}">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Value="False" Binding="{Binding MyBooleanValue}">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
Then, in your main visual tree you would define ContentControls which use those Styles, and assign the DataContext of the Window or UserControl or whatever to your ViewModel. Something like this:
<Grid>
<StackPanel >
<Button Content="False" Name="Button2"></Button>
<Button Content="True" Name="Button1"></Button>
<ContentControl Style="{StaticResource HiddenWhenFalse}">
<ContentControl.Content>
<TextBlock Text="ITS ALL TRUE"/>
</ContentControl.Content>
</ContentControl>
<ContentControl Style="{StaticResource HiddenWhenTrue}">
<ContentControl.Content>
<TextBlock Text="ITS ALL FALSE"/>
</ContentControl.Content>
</ContentControl>
</StackPanel>
</Grid>
Here is the ViewModel I'm using, note the implementation of INotifyPropertyChanged:
Imports System.ComponentModel
Public Class MainWindowViewModel
Implements INotifyPropertyChanged
Private _MyBooleanValue = False
Public Property MyBooleanValue
Get
Return _MyBooleanValue
End Get
Set(ByVal value)
_MyBooleanValue = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(Nothing))
End Set
End Property
Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class
Now, for the purposes of this sample, I simply wired up the two buttons to set the ViewModel value. If you want to use Commanding to wire up buttons, that's an entirely different topic. It's worth discussing, but for the sake of simplicity:
Class MainWindow
Private vm As New MainWindowViewModel
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
vm.MyBooleanValue = True
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button2.Click
vm.MyBooleanValue = False
End Sub
Private Sub MainWindow_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
Me.DataContext = vm
End Sub
End Class
Bear in mind that this sample explicitly styles a ContentControl, and that you'll have to change the TargetType of your style if you're working with a type that isn't descended from that class.