How can the ViewModel drive a ControlTemplate? - wpf

I have the problem that parts of the TreeView Controltemplate need to change depending on the state of the bound (ItemsSource) ViewModels. For example the little expander icon needs to be exchanged with a different drawing based on each items ViewModel state. Additonally based on the state of each ViewModel the child items need to be arranged horizontally instead of the default vertically.

It sounds like you have to customize ItemsContainerStyle, not ControlTemplate. For example, if you want to mark TreeViewItem as selected whenever underlying ViewModel is selected, you can use the following style:
<TreeView ItemsSource="{Binding ...}
...>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<!-- IsSelected is a property on ViewModel item -->
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
<Setter .../>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
If Binding doesn't suit you, you can use Converters, Triggers in Style or ControlTemplate. Furthermore, you can also use triggers in DataTemplates.
PS: Wrote the code from head. May have a typo.

Related

Toggle two different elements in DataTemplate using Style Triggers

I have an object in ViewModel whose properties are displayed by a datatemplate. The screen also has a button toggling the IsEditing flag in ViewModel, which should make the object properties editable, like the following:
Name should change from TextBlock to TextBox;
Color should change from colored rectangle to ComboBox with color options;
Category should change from TextBlock to ComboBox;
I know how to implement this with two completely independent DataTemplates, using a Style and a DataTrigger to toggle between them:
<ContentControl Content="{Binding FancyObject}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="ContentTemplate" Value="{StaticResource DisplayTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.IsEditing, ElementName=UserControl}" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource EditTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
And currently the DisplayTemplate is like this:
<DataTemplate x:Key="DisplayTemplate" DataType="my:FancyObject">
<Border>
<DockPanel DataContext="{Binding Metadata}">
<Border>
<TextBlock Text="{Binding Name}"/>
</Border>
<DataGrid
AutoGenerateColumns="False"
ItemsSource="{Binding FancyObjectCollection}">
<DataGrid.Columns>
<!-- Text and Template columns -->
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</Border>
</DataTemplate>
The problem is: using two independent but similar templates would mean a duplication of layout, since only some fields would change, but the overall structure is the same.
Another option I imagine is to use a single template defined inside the Style, and use the Trigger to change the fields individually, but I don't know how to do it, or even if it is possible at all.
You can use one template.
In the template add both TextBlock and TextBox, same for all your other controls on the original template.
Bind the controls visibility to bool to visibility converter. (Or use triggers) Only one set of your control will be seen each time (based on IsEditing flag)
The ControlTemplate is only used upon generating the UI Elements. If you change the template AFTER generating the items, the generated items will not change.
You can also not use a trigger to change a TextBox to a TextBlock and vice versa.
Your only option is indeed to mirror the layout twice and hide/display it via the data bound property.

how to set List item Height,width and Border for ListBox in wpf

I need to set Height,Width and Border Color for Each list items in the Listbox. In the event that I set Setter Property
it's setting for entire listbox. Be that as it may, I need to set every last rundown thing. Any one help me.
here's my code:
<ListBox x:Name="lbOne" PreviewMouseLeftButtonDown="ListBox_PreviewMouseLeftButtonDown"
HorizontalAlignment="Left" Margin="12,29,0,12" Width="215"
ScrollViewer.VerticalScrollBarVisibility="Visible" />
Try the following piece of code and let me know if it works for you.
<ListBox ...>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
Also, please refer the followings link for reference.
Stretch ListBox Items hit area to full width of the ListBox? ListBox style is set implicity through a theme
Silverlight 3: ListBox DataTemplate HorizontalAlignment
Probably, refer to the below link for using the ListBox Item properties appropriately.
http://msdn.microsoft.com/en-us/library/cc278062(v=vs.95).aspx

How to change wpf listviewitem background color when its data is using binding?

I'm trying to change listview's row background color for a data binding listview.
From msdn, I know there is ListViewsItem.Backgroud for the color of its background.
But since I'm using data binding for ListView's data source,the type of ListView's item is actually my class type, no longer ListViewItem. So I can't find its Background property.
I guess I missed something, how should I do it?
Thanks
You can set ItemContainerStyle of your listview like below to set any property on item.
<ListView>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Background" Value="Red"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
You even change the background depending of dataitem state using triggers in style.

Set parent controls property in xaml

I have a panel with a custom ListView. The ListView's items contains a GroupBox. The GroupBox contains a ListView. This ListView's items contains a GroupBox and so on.
All of the controls above have custom templates and styles
There are ToggleButtons in the Controls VisualTree lowest nodes.
When these buttons are checked I need to disable all the panel except of the button was clicked.
I would like to avoid event chaining through the parents in viewModel classes.
I'm using mvvm pattern and I would like to solve it in the xaml side if its possible.
EDIT: Here is a screenshot and the Pick button should disable the panel
Any suggestions are warmly welcomed.
you need to implement the relative source binding something like below.
IsEnabled="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}},Path=IsEnabled}"
Just have a read-only property in your ViewModel that is the negation of the property that your ToggleButton is bound to.
ViewModel:
private Boolean mSourceIsPicked;
public Boolean SourceIsPicked
{
get { return mSourceIsPicked; }
set
{
SetProperty("SourceIsPicked", ref mSourceIsPicked, value);
NotifyPropertyChanged("IsSourceChangeable");
}
}
public Boolean IsSourceChangeable
{
get { return ! this.SourceIsPicked; }
}
Then, in your View, just bind the IsEnabled property of the other controls to that new property.
<ComboBox ItemsSource="{Binding SourceTypes}"
IsEnabled={Binding IsSourceChangeable}" />
The advantage of binding to a property is that you can add/remove controls in your view and just bind to this property without changing additional XAML. You can also change the behavior of any control by not binding to this property.
If you really want a XAML-only solution, you can name each of the controls in the panel, and use a DataTrigger using TargetName on the "SourceIsPicked" property to disable the others:
<ComboBox x:Name="cboSourceTypes" ... />
<ComboBox x:Name="cboSourceNames" ... />
<ToggleButton>
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding SourceIsPicked}" Value="True">
<Setter TargetName="cboSourceTypes"
Property="IsEnabled"
Value="False" />
<Setter TargetName="cboSourceNames"
Property="IsEnabled"
Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
Note that this is all freehand, so you may need to adjust it a bit, but it gives you the idea.

How do I access ListViewItem?

The Items collection of a ListView contains the actual business objects. How do I obtain the corresponding ListViewItem given a business object (SelectedItem)?
If you really need to, use the ListView's ItemsContainerGenerator property. However, you can often get away with not setting an ItemContainerStyle with Bindings:
<ListView>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsSelected" Value="{Binding IsSpecial}"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
In the above XAML, the ListViewItems will be selected if the underlying bound object's IsSpecial property is true. Selecting/deselecting will update the IsSpecial property.

Resources