how to make a xaml control with different view modes - templates

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.

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>

Why h:selectBooleanCheckbox not is updated? [duplicate]

Here is the scenario (simplified):
There is a bean (call it mrBean) with a member and the appropriate getters/setters:
private List<String> rootContext;
public void addContextItem() {
rootContext.add("");
}
The JSF code:
<h:form id="a_form">
<ui:repeat value="#{mrBean.stringList}" var="stringItem">
<h:inputText value="#{stringItem}" />
</ui:repeat>
<h:commandButton value="Add" action="#{mrBean.addContextItem}">
<f:ajax render="#form" execute="#form"></f:ajax>
</h:commandButton>
</h:form>
The problem is, when clicking the "Add" button, the values that were entered in the <h:inputText/> that represent the Strings in the stringList aren't executed.
Actually, the mrBean.stringList setter (setStringList(List<String> stringList)) is never called.
Any idea why?
Some info -
I'm using MyFaces JSF 2.0 on Tomcat 6.
The String class is immutable and doesn't have a setter for the value. The getter is basically the Object#toString() method.
You need to get/set the value directly on the List instead. You can do that by the list index which is available by <ui:repeat varStatus>.
<ui:repeat value="#{mrBean.stringList}" varStatus="loop">
<h:inputText value="#{mrBean.stringList[loop.index]}" />
</ui:repeat>
You don't need a setter for the stringList either. EL will get the item by List#get(index) and set the item by List#add(index,item).

using observablecollection and cxml in 1 pivotviewer

I am new to pivotviewer and also to silverlight. I have created a observablecollection which has
the properties
public string StaffName { get; set; }
public string Location {get;set;}
using a method BuildCollection() I bind it to mainpage.xaml in the following manner:
<pivot:PivotViewer x:Name="pViewer">
<pivot:PivotViewer.ItemTemplates>
<pivot:PivotViewerItemTemplate MaxWidth="300">
<Border Width="300" Height="300"
<TextBlock Text="{Binding StaffName}"
FontSize="90"
FontWeight="Bold"
Foreground="White"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</pivot:PivotViewerItemTemplate>
</pivot:PivotViewer.ItemTemplates>
</pivot:PivotViewer>
Now I want to bind the CXML file cities.cxml which resides in the clientbin which has the images and also the property called Location. I want to create a link between the collection and the cxml so that when i deepzoom the collection which shows the staffname it should fade into the image of the city which is in the cxml collection. for ex Location is london in observable collection I should be deepzoom it to the image of london from the cxml file. Can anyone help in achieving this? Thanks.
First the observable collection was bound. Here I added a property called img in the observable collection which represents the image index in the xml file
I bound the xml file to another template and in the subimagehost I bound the img index therefore on deepzooming the observable collection I got the image also
<pivot:PivotViewerItemTemplate>
<Grid Width="800" Height="800" Background="LightGray" MaxWidth="800">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Rectangle Fill="{Binding StaffName, Converter={StaticResource xconv}}"/>
<pivot:PivotViewerMultiScaleSubImageHost CollectionSource="http://localhost:65535/ClientBin/Map_files/wr5bbwrn.dre.xml" ImageId="{Binding img}" >
</pivot:PivotViewerMultiScaleSubImageHost>
<TextBlock Text="{Binding Vehicle}"
FontSize="40"
Margin="150,200,150,150"
Grid.Column="1"
VerticalAlignment="Bottom"
HorizontalAlignment="Center"/>
</Grid>
</pivot:PivotViewerItemTemplate>

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

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.