I have a collection of objects that have different rendering options: They can be simple text, editable text, comboboxes, or event a mixed bag (like a comboBox where items are usually text but with images for specific values).
I managed to show everything correctly by using ContentTemplateSelector inside a ContentPresenter node inside the DataTemplate of the ListViewItem.ItemTemplate:
<ContentPresenter
Grid.Row="1"
Grid.Column="1"
Content="{Binding .}"
ContentTemplateSelector="{DynamicResource ResourceKey=RenderTemplateSelector}"/>
(note that everything is inside the DataTemplate of a ListView.ItemTemplate)
All is good until I change the value of said property and need to change the template, for instance going from a image to a text.
The VM does update correctly the values, but nothing happens in the GUI.
Looking around here there are a few methods (using a Converter (bound to which property??), defining a Style (i think is not relevant, since i must show different controls, not change properties of the same one), using an AttachedProperty (I don 't understand really well how attached properties work, but from what I saw I should have a ContentControl node, which I don't...) but nothing seems what I need.
So, a recap: I have a ListView that has a DataTemplate definded for each ListViewItem that contains another DataTemplate that needs to change based on a properti of the ListViewItem object. I managed to achieve this with a ContentTemplateSelector, but that is assigned at the beginning and then never changed.
You could try to replace the ContentPresenter with a ContentControl that has a Style with data triggers that sets the ContentTemplate, .e.g.:
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding YourProperty}" Value="Type1">
<Setter Property="ContentTemplate" Value="{StaticResource template1}" />
</DataTrigger>
<DataTrigger Binding="{Binding YourProperty}" Value="Type2">
<Setter Property="ContentTemplate" Value="{StaticResource template2}" />
</DataTrigger>
<!-- ... -->
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
Related
I am running into a problem with WPF UI wherein I need to set the border of a GroupBox control based on the fact if there is any checkbox inside the GroupBox that has been unchecked. I have tried writing some code but that didn't work, I am new to WPF and I tried using a style to solve the problem but that didn't work well with me. The code looks as follows -:
<GroupBox Grid.Row="6" Grid.ColumnSpan="3" Header="View Actions" >
<GroupBox.Style>
<Style TargetType="GroupBox" d:DataContext="{d:DesignInstance ActionsViewModels:IActionViewModel}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsEnabled}" Value="False">
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="2"/>
</DataTrigger>
</Style.Triggers>
</Style>
</GroupBox.Style>
<ContentControl Content="{Binding ViewActionsVM}"/>
</GroupBox>
Just FYI, the is used to populate the GroupBox with objects which have a text and checkbox component inside them and look something like this
Now what I want to be able to do is when one unchecks this checkbox I want to be able to add a red border around the Groupbox in which its contained. The content shown in the image comes from the ContentControl binding that we have in our project. To add to the description of the Content, the content describes an Action to be performed and has a property called as IsEnabled that I am using in the DataTrigger to decide whether to add a border or not.
I have a functional binding on my TextBox which acts as a searbar.
However, when I add the following trigger applied on the same binding (The first trigger), it stops the binding.
<TextBox>
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Text" Value="{Binding Path=SearchFilterString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Style.Triggers>
<!-- 1st trigger -->
<DataTrigger Binding="{Binding Path=SearchFilterString}" Value="">
<Setter Property="Text" Value="Type in part name to search."/>
</DataTrigger>
<!-- 2nd trigger -->
<Trigger Property="IsFocused" Value="true">
<Setter Property="Text" Value="{x:Null}"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
I also included the second trigger, to make sure after I fix the problem with the first one it won't create a endless loop situation. So please comment on that one as well.
The purpose of these two triggers is to show some guidline inside the TextBox to describe what is this texbox about and the guidline disapears as soon as TextBox gains focus and user attempts to type in the search keyword.Let me know if you believe there is a better approach on doing the same thing.
Your binding won't work, because you are overriding it with your trigger's setter: You are setting the Text property of it to a value, while you are binding the same Text property to the view model. Both things can't work at the same time.
Essentially you're trying to create something that is called "watermark" on a textbox. I like the following the solution in particular, because it doesn't modify the TextBox itself but layers an adorner ontop of it:
Watermark Behavior for TextBox
You are looking for a watermark textbox. The standard WPF TextBox does not support this behavior.
However, there are 3rd party controls available such as the one pointed out by Frank: http://wpfplayground.com/2014/06/30/watermark-behavior-for-textbox/
Ways to do your own can be found at Watermark / hint text / placeholder TextBox in WPF
Finally, you can always just overlay your own TextBox or TextBlock over the real one and hide it on focus, which will do basically the same thing.
This is how I achieved it using Brad's idea.
I simply put a Label with the watermark I wanted and overlapped my TextBox on top of it. All I am doing is setting the TextBox's Background property to Transparent when it's binding content is empty. When this happens you can see the label on the back.
Hence, the moment you start typing watermark disappears.
XAML
<!-- Watermark Label -->
<Label
Foreground="Gray"
Content="Type the part name to start the search."
/>
<!-- Search TextBox -->
<TextBox Grid.Column="0"
HorizontalAlignment="Stretch"
>
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Text" Value="{Binding Path=SearchFilterString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SearchFilterString}" Value="">
<Setter Property="Background" Value="Transparent"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Outcome
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.
I have got ComboBox which is populated with collection of customTypes. On comboBox change, I would like to load/change content in the particular region so that it loads related data for the selected ComboBox item (it could be in the form of loading userControl or I i dont mind specifying DataTemplate for it).
It is similar to this question WPF Load Control question. But in this question, he is talking about individual DataTemplates in the actual Listbox, while I am talking about populating certain window region on ComboBox change.
I am using MVVM (and not PRISM) .Net 3.5.
U could use ContentControl which is the placeholder for the actual Content that is dynamically decided as per combobox selection.
The follwoing code is just for guidance
<Window ...>
<Window.Resources>
<MyView1 x:Key="MyView1" />
<MyView2 x:Key="MyView2" />
</Window.Resources>
...
<ComboBox x:Name="MyCombo">
<ComboBox.ItemsSource>
<sys:String>"MyView1"</sys:String>
<sys:String>"MyView2"</sys:String>
....
</ComboBox.ItemsSource>
</ComboBox>
...
<!-- This is where your region is loaded -->
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SelectedItem,
ElementName=MyCombo}"
Value="MyView1">
<Setter Property="Content"
Value="{StaticResource MyView1}"
</DataTrigger>
<DataTrigger Binding="{Binding Path=SelectedItem,
ElementName=MyCombo}"
Value="MyView2">
<Setter Property="Content"
Value="{StaticResource MyView2}"
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</Window>
The data loading can be part of the MyView1 and MyView2 user control's constructor or your main UI's data context view model.
As far as I understand the question is how to change underlying data being bound to UI and not a DataTemplate only.
You can use EventSetter which will be handled in code behind where you can switch DataContext for a region you've mentioned:
<ComboBox>
<ComboBox.Resources>
<Style TargetType="ComboBoxItem">
<EventSetter Event="Selector.SelectionChanged"
Handler="YourHandler"/>
</Style>
</ComboBox.Resources>
</ComboBox>
But from MVVM perspectives it could be not perfect solution so you can introduce your own ComboBox class wich is Command aware, see this SO post: WPF command support in ComboBox
In this way you can decouple logic from UI using Command.
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.