I wonder what the best design is to show different content dynamically in a list view.
What I want is (e.g.) depending on a radio button, a list view be filled with items (setting ItemsSource, changing column width, names and the row information itself).
What I'm currently doing is dynamically changing all the list view properties.
I wonder if it would be better to use multiple list views (maybe even as user controls), all on the same window position and hide all non-used.
Also, since I'm using MVVM Light/WPF I want to easily connect it to the XAML code if possible.
If changes in ItemsSource occur in time (or you want change it depending on some conditions like RadioButton's IsChecked property) you need to reflect on change your DataTemplate. The way out is to create DataTrigger which will swap your DataTemplates based on some criteria.
<ListView>
<ListView.Style>
<Style TargetType="ListView">
<Setter Property="ItemsSource" Value="{Binding InitalSource}"/>
<Setter Property="ItemTemplate" Value="{StaticResource InitialDataTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=RadioButton, Path=IsChecked}" Value="True">
<Setter Property="ItemsSource" Value="{Binding AnotherSource}"/>
<Setter Property="ItemTemplate" Value="{StaticResource AnotherDataTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Style>
</ListView>
Whenever condition of DataTrigger stops being true all changes made by Setters vanish, by default, therefore you do not need to add another DataTrigger in case of False value for RadioButton's IsChecked.
Related
I have a requirement to display around 50 Labels that would change background color depending on the value of a view model boolean property. Each Label is associated with a different view model boolean property. How can I create a single style to do this that I can associate with all 50 labels, so that I don't have to declare a style for every label. Is there a way I can apply a SINGLE style and/or data trigger to ALL 50 labels, since each label will be bound to a different view model boolean property.
I am well aware of how global styles work. My SPECIFIC question: is there a way I can apply a SINGLE style or data trigger to ALL 50 labels, since each label will be bound to a different view model boolean property.
No. You cannot dynamically change the binding of a DataTrigger unless you create it programmatically.
I am afraid there is no way to dynamically just replace the binding path of a DataTrigger and re-use the rest of the style or template in XAML.
I think you can try to create a custom Template for the Labels and add ControlTemplate trigger to change the background of label.
<Style x:Key="test" TargetType="Label">
<Setter Property="BorderThickness" Value="1"></Setter>
<Setter Property="BorderBrush" Value="Black"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<TextBlock Text="test"></TextBlock>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ColorValue}" Value="12">
<Setter Property="Background" Value="Aquamarine"></Setter>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Above code is just to show the usage, it is not the exact answer.
I have an ItemsControl that is bound to an ObservableCollection. This collection has a validation rule (using IDataErrorInfo) that basically says 'If there is a 'Quote' in this list, then there has to be a 'Customer' as well.
I can see that the validation rule is working as expected as I have a button bound to a command that can't execute if there are errors. However, the ItemsControl is not visually updating with the error style.
This error style does work. I'm using it on a number of other controls (mainly TextBox and ComboBox) and it does show on the ItemsControl if the bound collection is set to a new collection. It is purely on adding and removing that the error style does not update.
<ItemsControl Margin="5" ItemsSource="{e:Binding Path=CurrentBooking.Regardings}" Background="Transparent">
<!-- Items Panel and ItemTemplate Stuff -->
</ItemsControl>
e:Binding is a custom Binding that, amongst other things, sets the 3 ValidatesOn... properties to true. CurrentBooking.Regardings is the ObservableCollection
This is the empty user control. The plus icon is used to add items to the collection.
This should have the error style applied as there is a quote and no customer. The button mentioned above is disabled, showing that there is an error.
If I instead create a new collection with the added item, it does apply the style. However, I don't particularly want to create a new collection every time an item is added or removed.
Code for error style:
<Style x:Key="ErrorStyle" TargetType="FrameworkElement">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
</Trigger>
<!--Clear Template if false-->
<Trigger Property="Validation.HasError" Value="false">
<Setter Property="Validation.ErrorTemplate" Value="{x:Null}"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="ItemsControl" BasedOn="{StaticResource ErrorStyle}">
<Setter Property="Background" Value="Transparent"/>
</Style>
Is there a way to do this, or do I have to create an IsSelectedProperty on the ViewModel and Bind to that instead?
I would like to be able to do something like Source={Binding RelativeAncestor ListViewItem}
but instead there is only this property sourcename which I can use to set triggers based off of the items in the datatemplate if I name them using x:Name
<HierarchicalDataTemplate.Triggers>
<Trigger SourceName="" Property="IsSelected" Value="True">
<Setter TargetName="bdr" Property="Foreground" Value="White"/>
<Setter TargetName="bdr" Property="Foreground" Value="Red"/>
</Trigger>
</HierarchicalDataTemplate.Triggers>
Looking back, I realized that yesterday I made a very confusing post. Also, considering how hard I find it to even interpret people's comments to my questions, I should probably give more detail.
I know that in wpf you can set triggers based on the control by setting a style for the target control type.
In addition you also have data triggers that can trigger off of properties in the datacontext. What I was hoping was that there was a way to use triggers, or datatriggers to set a property when the datacontext is an object of a specific type.
is this possible? If not, I will just accept the provided answer as applicable in my situation, but since this will require me to add a property to my viewmodel, it seemed reasonable to check if there was a way to just check item type rather than having to check the actual property.
I would suggest that you bind the IsSelected property in your view model, but that's just me.
I'm not sure how complex your HierarchicalDataTemplate is, or if some items need to have their Foreground changed, and some don't; but I'm going to assume you want to update the item that is selected throughout the entire TreeView (if that's what this is for).
If that is the case, just add DataTriggers to the Style of the TreeViewItem:
<Style TargetType="TreeViewItem">
<Setter Property="IsSelected"
Value="{Binding IsSelected, Mode=TwoWay}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Foreground"
Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding IsSelected}"
Value="False">
<Setter Property="Foreground"
Value="Black" />
</DataTrigger>
</Style.Triggers>
</Style>
Note that you don't need to bind the IsSelected parameter if you don't want to, its just there because it's in my code.
I want to make my gridsplitters in a custom user control have visibility collapsed
based on when the parent control has loaded some data using a load button? I thought that the way to do this would be to create a property dataloaded on the parent control and then set a trigger in the triggers of the usercontrol like below:
but I can't seem to get it to reference the property of the usercontrol (graphviewer).
Also, can property triggers reference other controls within the control like I did below? I am assuming either my syntax is wrong or what I am trying to do is not possible. So Far I have only messed with a few basic properties within trigger templates when making modifications to control templates, so I don't really know whether what I am trying to do is possible.
<UserControl.Triggers>
<Trigger Property="GraphViewer.DataLoaded" Value="true">
<Setter Property="SignalNameGridSplitter.Visibility" Value="Visible" />
</Trigger>
</UserControl.Triggers>
Try this... first add the XML Namespace of your GraphViewer control - something like this:
xmlns:YourXmlNamespace="clr-namespace:YourApplicationName.FolderNameIfApplicable"
Then add this into a Style... it has to be in a Style.Triggers collection because you can't use a DataTrigger in a UserControl.Triggers collection:
<UserControl.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding DataLoaded, RelativeSource={RelativeSource
FindAncestor, AncestorType={x:Type YourXmlNamespace:GraphViewer}}}" Value="True">
<Setter Property="SignalNameGridSplitter.Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Style>
I'm currently implementing a listbox in WPF that will have 2 alternative layouts for its items:
So far, I've done this using a DataTrigger to switch the ItemTemplate for the ListBox and it's working well:
<ListBox ItemsSource="{Binding Runs}" SelectedItem="{Binding SelectedRun}">
<ListBox.Style>
<Style TargetType="ListBox">
<Setter Property="ItemTemplate" Value="{StaticResource tileTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ShowRunsAsIcons}" Value="True">
<Setter Property="ItemTemplate" Value="{StaticResource iconTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>
However, the Runs collection to which the list is bound will also contain different types of object:
interface IRunItem
{
// ...
}
class CompletedRunItem : IRunItem
{
// ...
}
class PendingRunItem : IRunItem
{
// ...
}
Each of the object types should have its own 'tile' and 'icon' templates (making 4 templates in total). What's the best way of switching on these two properties to according to the boolean ShowRunsAsIcons and the type of the list item?
I've considered using a pair of DataTemplateSelector subclasses -- one to choose between tile templates based on item type, and one to choose between icon templates based on item type -- but this just feels horribly clunky. I feel as though I should be taking advantage of WPF's ability to choose the correct template based on the object's type, but in this instance, I don't see how to combine that with the list's different view options.
Any ideas of how to do this that's more in the spirit of WPF?
Thanks.
Although I'm not convinced it's the best answer, I've changed my approach to take advantage of WPF's automatic template selection. I now have 'top-level' data templates defined for each of my concrete data classes.
These data templates contain nothing but a ContentControl whose ContentTemplate property is set via a DataTrigger, binding to the data context's ShowRunsAsIcons property.
As an example, here's the keyless data template for PendingRunItem:
<DataTemplate DataType="{x:Type Common:PendingRunItem}">
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Setter Property="ContentTemplate" Value="{StaticResource pendingTileTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.ShowRunsAsIcons, RelativeSource={RelativeSource FindAncestor, AncestorType=ListBox}}" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource pendingIconTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
The icon and tile representations for the relevant classes are then just regular data templates. And the ListBox no longer needs its Style property defined:
<ListBox ItemsSource="{Binding Runs}" SelectedItem="{Binding SelectedRun}"/>
I'd be interested to know people's thoughts on this approach and its benefits and drawbacks when compared to using a DataTemplateSelector or two.