Having trouble setting visibility of control through DataTemplates - c#

So, I'm building an order tracking app with different user accounts, some of whom have less need-to-know than others. This means that certain controls are displayed for some accounts, and hidden for others.
The datacontext for the Window is set to my Order class, and the data binding within the text fields works perfectly in regards to displaying properties from the specific Order. However, the DataTemplates and Triggers I've made don't seem to be doing anything at all, and I'm not entirely sure why. I've looked all over the web and I can't seem to find why it's not working. Here's the XAML:
<Label Name="StatusLabelText" Content="Status:" FontSize="15" DockPanel.Dock="Top">
<Label.Resources>
<DataTemplate DataType="x:Type local:Order">
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=selectedAccount}" Value="Color Correct">
<Setter Property="Visibility" Value="Hidden"></Setter>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Label.Resources>
</Label>

I suspect you want to hide label in case selectedAccount value is Color Correct.
You need Style to do that and not a template if my assumption is correct which can be done like this:
<Label Name="StatusLabelText" Content="Status:" FontSize="15"
DockPanel.Dock="Top">
<Label.Style>
<Style TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=selectedAccount}"
Value="Color Correct">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
On a side note, you should use Collapsed instead of Hidden to set visibility of control in case you don't want the label to take the size even when it's not visible on GUI. Read more about it here.

Related

Can I change the color of the text of my property when using mvvm?

I'm new here, so if there's anything I'm doing wrong or need to do differently, I'd love to hear it.
I have a property error message that I "binded" with the xmal. This property returns 2 strings. Either a message that something hasn't been entered or a calculation.
Can I give red with one and black with the other?
Thank you in advance,
this is the method
Property
xmal
You can use a DataTrigger inside a Style for the label.
<Label x:Name="lblFoutmelding" FontWeight="SemiBold" Content="{Binding Foutmelding" Grid.Row="3" Height="30" Margin="9 10 0 0">
<Label.Style>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Black"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Foutmelding}" Value="Gelieve de hoeveeleed in ta geven aub!">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</Style.Triggers>
<Style>
</Label.Style>
</Label>
Note that I have removed the black into a style setter, since setting it directly on the Label would override any style or trigger setters.
Also note that it would be much better to have a separate "Severity" property in your VM and trigger on that, rather than triggering on specific strings.

StackPanel visibility depending on Combobox selection

I want to have a StackPanel who's visibility should be depending on a Combobox selection. Unfortunatly the XAML below does not work.
I found a solution with a new property which will be set on the PropertyChanged event of the Combobox selection, though I would prefer a strict XAML solution for this.
Any hints on how to solve this?
<StackPanel>
<Label>Picture in Picture function</Label>
<ComboBox Name="cbPictureInPicture" ItemsSource="{Binding Path=PictureInPictureCodeList, Mode=OneWay}" DisplayMemberPath="CodeText"
SelectedValuePath="CodeID" SelectedValue="{Binding Path=PictureInPicture, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
<StackPanel>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=cbPictureInPicture, Path=IsSelected.CodeText}" Value="Yes">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<Label>Picture in Picture is used</Label>
(...)
</StackPanel>
you may perhaps rewrite the same as
<DataTrigger Binding="{Binding ElementName=cbPictureInPicture, Path=SelectedItem.CodeText}" Value="Yes">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
assuming the combobox is bound to a collection whose item has CodeText property. so SelectedItem.CodeText will point to the same.
additionally it may not be required to set <Setter Property="Visibility" Value="Visible" /> as it is the default value. it does not have any effect in this case just some extra line of code which can be removed.
You can also use a converter and bind directly to the PictureInPicture property:
<StackPanel Visibility="{Binding PictureInPicture, Converter={StaticResource myVisibilityConverter}}"/>
<Label>Picture in Picture is used</Label>
(...)
</StackPanel>
Create flags and pass this flag in stackpanel visibility converter.
On the basis of flag in converter make decision stackpanel visible/hide whatever
Set this flat in comboBox selection change event if selected value as per your requirement.

Binding an empty string if value is non-null in wpf

I would like to make a WPF usercontrol that shows a string when and only when the datacontext == null. I'm using the TargetNullValue attribute in binding to display a custom string when the datacontext is null, and that's working as intended. But when the datacontext is non-null, it just shows the ToString value, which I would like to get rid of.
Of course I could solve this easily by using a valueconverter, but I'd like to find a way to solve this with xaml only. Does anyone know a way to do it?
In case you want TextBlock to be shown only in case binding value is null, you can have trigger in place and set Visibility to Visible when binding value is null and otherwise Collapsed always.
<TextBlock Text="{Binding TargetNullValue=NullValue}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding}" Value="{x:Null}">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Use a data trigger on {x:Null}. There are many options using styles, data templates etc., depending on taste and needs. For instance:
<DataTemplate x:Key="ShowOnNull">
<TextBlock x:Name="text"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding}" Value="{x:Null}">
<Setter TargetName="text" Property="Text" Value="your custom string"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
...
<ContentPresenter ContentTemplate="{StaticResource ShowOnNull}"
Content="{Binding ...}"/>

How to switch between views using DataTemplate + Triggers

I have a requirement where a where user can switch to view hierarchical data either as tree or as a text in datagrid or as FlowChart.
The user can do this by clicking a Toggle Button which say: Switch Mode. I want to do all this in such a way that it can be handled within the View only as ViewModel in all the three cases is the same.
How do I apply View to my ViewModel based on Trigger.
If the state of which view to show is saved in some enum property you could use a ContentControl and DataTriggers for example:
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding ViewMode}" Value="TreeMode">
<Setter Property="Content">
<Setter.Value>
<uc:TreeModeView />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ViewMode}" Value="GridMode">
<Setter Property="Content">
<Setter.Value>
<uc:GridModeView />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
(As the style is only used in one place, by setting it directly as ContentControl.Style this will work, if you want to use it in more than one place you should set the ContentTemplate instead, because otherwise there will only be one view instance shared by all controls with the style which is not allowed by WPF (of course Content needs to be set to something for the template to be applied))
You could also bind directly to IsChecked of the ToggleButton using ElementName of course. The relevant values would then be True, False and {x:Null}.
H.B.'s answer is good, but there are scenarios where it's not quite so good.
If constructing the views (and their underlying view models) is expensive, then toggling the Content property will pay this expense every time the user changes the view.
In some scenarios, it makes sense to create both views in the same container (e.g. a Grid), and toggle their Visibility instead. If you use lazy evaluation in your view models, the expensive operations won't be conducted until the view becomes visible, and - importantly - they'll only be conducted the first time the view becomes visible. Once both views have been displayed, the user can toggle back and forth between views without reconstructing the underlying view models.
Edit:
I stand corrected, sort of: H.B.'s answer is not as good as it looked .
You can't use a style to set the Content property of a ContentControl to a UIElement. See this blog post for full details, but the long and short of it is that if you use H.B.'s approach, you'll get a runtime error.
You can set the ContentTemplate property, instead, e.g.:
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding ViewMode}"
Value="TreeMode">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<uc:TreeModeView/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ViewMode}"
Value="GridMode">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<uc:GridModeView/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>

How to dynamically inject a user control based on user selection

I have a TreeView control on my window that depending on which selection the user picks should display a user control (figuring out which user control to display is complete). I'm having issues figuring out how to actually display the user control. Essentially, the user would select item from the TreeView and based on the selection a user control would appear (in a ContentControl control I'm assuming).
Currently, for opening new windows I have a window adapter where I can create new windows on the fly and set the parent.
How can I accomplish this in my view model?
Edit
Here is what I believe Rachel was talking about when she mentioned using DataTemplate's instead. Don't be worried of my DataTemplates instead the DataType attribute. That's simply the name of my project.
<Window.Resources>
<DataTemplate DataType="{x:Type DataTemplates:FooEditorViewModel}">
<DataTemplates:FooControl></DataTemplates:FooControl>
</DataTemplate>
<DataTemplate DataType="{x:Type DataTemplates:BarEditorViewModel}">
<DataTemplates:BarControl></DataTemplates:BarControl>
</DataTemplate>
</Window.Resources>
And here is a sample view model.
public class ViewModel
{
public IEditorViewModel Editor
{
get
{
return new BarEditorViewModel();
}
}
}
And glue it all together with
<ContentControl Content="{Binding Editor}" />
I had to create a blank interface called IEditorViewModel in order to return different user control editors. Not sure if there's any way around it.
Hopes this helps someone.
Your SelectedTreeViewItem would be stored in your ViewModel, and that value would be used to determine which item to display.
An example would be something like:
<ContentControl Content="{Binding SelectedItem}">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource DefaultTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItem}" Value="{x:Type local:ItemA}">
<Setter Property="ContentTemplate" Value="{StaticResource TemplateA}" />
</DataTrigger>
<DataTrigger Binding="{Binding SelectedItem}" Value="{x:Type local:ItemB}">
<Setter Property="ContentTemplate" Value="{StaticResource TemplateB}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
A better alternative is to use DataTemplates for different Items. Then you just set your Content="{Binding SelectedItem}" and WPF will resolve the correct DataTemplate to use. I just showed the above example first because it can be used to base your Template off a Property of SelectedItem

Categories