Bind enum value to a property using style setter - wpf

I have the following code. Please look at Radio Button Style, I want to bind the value of the enum to a property in the ViewModel when the radio button is checked. I have property called SelectedWellType and I want to bind to it. But it says a binding cannot be set on Property of Type Property in Setter. Please help.
<RadioButton
Name="IndividualWellsRadioButton"
Grid.Row="0"
KeyboardNavigation.IsTabStop="True"
KeyboardNavigation.TabIndex="1"
KeyboardNavigation.AcceptsReturn="True"
Content="{StaticResource IndividualWells}"
IsEnabled="{Binding IsIndividualWellAutoAnalysisPossible}">
<RadioButton.IsChecked>
<Binding
Converter="{StaticResource enumToBoolConverter}"
ConverterParameter="{x:Static dataDef:AutoAnalysisModes.IndividualWells}"
Path="AutoAnalysisMode" />
</RadioButton.IsChecked>
<RadioButton.Style>
<Style
TargetType="{x:Type RadioButton}">
<Style.Triggers>
<Trigger
Property="IsChecked"
Value="True">
<Setter
Property="{Binding SelectedWellType}"
Value="{x:Static dataDef:AutoAnalysisModes.IndividualWells}"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</RadioButton.Style>
</RadioButton>

Related

Apply ValueConverter in style when source comes from x:Static resx file

After quite some search and reading other questions & posts, I was not able to find how to solve this. Note: I'm relatively new to WPF (not to binding in general).
Here's what I'm after:
I'd like to have all TextBlock controls in a window to be styled in some way.
The style should also apply a ValueConverter to make text all uppercase.
Finally, each TextBlock Text could come either from binding to a view-model property, or from binding to a resource string from a .resx file
Here's an excerpt of what I'm playing with:
<!-- in Window resources -->
<help:AllCapsStringConverter x:Key="AllCaps"/>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Brown" />
<Setter Property="Text">
<Setter.Value>
<Binding>
<Binding.Converter>
<help:AllCapsStringConverter />
</Binding.Converter>
<!-- also tried adding:
<Binding.RelativeSource>
<RelativeSource Mode="Self" />
</Binding.RelativeSource>
-->
</Binding>
<!-- also tried with:
<Binding Converter="{StaticResource AllCaps}"/>
<Binding Path="Text" Converter="{StaticResource AllCaps}"/>
-->
</Setter.Value>
</Setter>
</Style>
<!-- in Window content -->
<TextBlock Text="{x:Static resx:MyResources.MyTitle}" />
Here's the value converter, which on its own has proved to be working:
class AllCapsStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null && value is string)
{
return ((string)value).ToUpper();
}
else
{
return value;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
TextBlocks get the foreground color, but no conversion kicks in.
I was able to apply the converter locally to the single TextBlock, but I don't want to apply that to all TextBlocks around the window:
<TextBlock>
<TextBlock.Text>
<Binding Source="{x:Static resx:MyResources.MyTitle}"
Converter="{StaticResource AllCaps}"/>
</TextBlock.Text>
</TextBlock>
<!-- which is the same as -->
<TextBlock Style="{StaticResource CustomerInfoTitleStyle}"
Text="{Binding Source={x:Static resx:MyResources.MyTitle}, Converter={StaticResource AllCaps}}" />
Your converter is not working because your TextBlock is overriding the Text property of the Style, which includes the converter you have added to the binding.
For example:
<Grid.Resources>
<Style x:Key="MyTextBlockStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="Text" Value="You won't see this."></Setter>
</Style>
</Grid.Resources>
<TextBlock Text="You will see this." Style="{StaticResource MyTextBlockStyle}"/>
Hopefully from the above you can see why your approach is not working.
A better solution would just be to set the value of Text with the value converter on the TextBlock, rather than in the Style.
If you don't want to do that, one common cheat you could use to get around this could be to bind the TextBlock's Text property to the Tag property, like so:
<Grid.Resources>
<local:AllCapsConverter x:Key="AllCaps"/>
<Style x:Key="MyTextBlockStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="Text" Value="{Binding Tag, RelativeSource={RelativeSource Self}, Converter={StaticResource AllCaps}}"/>
</Style>
</Grid.Resources>
<TextBlock Tag="You will see this." Style="{StaticResource MyTextBlockStyle}"/>
I have a strong dislike of this approach, but it does get you what you want. I would prefer you just use the converter when you set the binding on the TextBlock.
lukegv's approach is another alternative. However, there is no need to use a Label, as you are overriding the template and (similar to my example above) you are just binding to the Content property of the Label. You could just as easily get what you need from a ContentControl.
<Grid.Resources>
<local:AllCapsConverter x:Key="AllCaps"/>
<Style x:Key="MyContentControl" TargetType="ContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<TextBlock Text="{Binding Content, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource AllCaps}}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<ContentControl Style="{StaticResource MyContentControl}" Content="You will see this too!"/>
I'm not a particularly huge fan of this idea either though, as you lose access to all the other TextBlock properties. For example, if I wanted to set the FontWeight property of my TextBlock then I would be stuffed.
Try to use a 'Label' and build a template, because a 'TextBlock' is not a control.
<Style x:Key="AllCapsLabel" TargetType="{x:Type Label}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<TextBlock Foreground="Brown" Text="{Binding Content, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource AllCaps}}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And use it this way:
<Label Style="{StaticResource AllCapsLabel}" Content="whatever you want" />
If the content is plain text (aka a 'String'), the text should always be uppercase.

Datatrigger on contentpresenter.content not working

I am trying to switch the content of a contentpresenter based on a datatrigger.I want to display a usercontrol in the contentpresenter.content, if i have a value set or else i need to display an error message.But the binding on my datatrigger fails stating that the property is not found.I cant get the datacontext to inherit for the datatrigger checking.I can make it work by using the commented out code.But i am confused why it doesn't work the normal way.
<ContentPresenter.Style>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="Content" Value="{Binding UC}"/>
<Style.Triggers>
<!--<DataTrigger Binding="{Binding DataContext.HasValue,RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}" Value="false">
<Setter Property="Content" Value="No preview"/>
</DataTrigger>-->
<DataTrigger Binding="{Binding HasValue}" Value="false">
<Setter Property="Content" Value="No value"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentPresenter.Style>
</ContentPresenter>
If you want to use triggers to display UserControl, you should use ContentControl not ContentPresenter.
I prefer to use ContentPresenter for CustomControls and When I am using the UserControl for views of Custom Data Types in my system and Allow to give dynamic behavior.
Example: To switch templates for ContentPresenter you need to set ContentTemplateSelector like this
<ContentPresenter Content="{Binding MyContent}"
ContentTemplate="{Binding MyContentTemplate}"
ContentTemplateSelector="{Binding MyContentTemplateSelector}"/>
MyContent, MyContentTemplate & MyContentTemplateSelector are Dependency Properties and can be binded wherever you are using its instance.
READ :
Usage of ContentPresenter
What is the difference between ContentControl and ContentPresenter
The binding mentioned in the question won't work as
ContentPresenter’s DataContext is automatically set to the value of
its Content property, while ContentControl’s DataContext is not.
Bindings are resolved relatively to the value of the DataContext property. If you declare a binding on the ContentPresenter, the moment its content is set, the binding would be re-evaluated.
ContentControl.Content Property can be changed on any trigger based on your requirement. If you want to use it to change on PropertyChanged Event of a property of ViewModel, DataTrigger can be used by binding it with a DataTemplate with UserControl instance in it or using static resource of that UserControl.
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Value="{StaticResource UnSelectedDataTemplate}" Property="ContentTemplate" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected}" Value="True">
<Setter Value="{StaticResource SelectedDataTemplate}" Property="ContentTemplate" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentContro.Style>
</ContentControl>
READ How to use triggers for content template, more details here
Difference in DataTemplate and StaticResource scope is DataTemplate creates a new instance of the template every time its applied.
Whereas, StaticResource is using the same instance of UserControl again (Static Instance).
You can also use EventTriggers to change content base don Control Events like MouseOver etc.
Alternate approach
Very similar to the above with slight difference. Defining as a data template in resources. Triggering for the content change is essentially identical.
...in <x.Resources /> tag:
<DataTemplate x:Key="DesignerTemplate" DataType="{x:Type vm:SolutionViewModel}">
<vw:SolutionDesignerView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SolutionViewModel}">
<ContentControl Content="{Binding }">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsLoaded}" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource DesignerTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
...then:
<ContentControl Content="{Binding Solution}" />
I usually use trigger Like this...
<UserControl>
<UserControl.Resources>
<DataTemplate x:Key="normalTemplate" >
<!-Fav UserControl->
</DataTemplate >
<DataTemplate x:Key="overWriteTempalte">
<!-Fav UserControl-> </DataTemplate>
</UserControl.Resources>
<ContentPresenter x:Name="ContentField"
Content="{Binding}"
ContentTemplate="{StaticResource ResourceKey=normalTemplate}" />
<UserControl.Triggers>
<DataTrigger Binding="{Binding Path=MyProperty}" Value="True">
<Setter TargetName="ContentField" Property="ContentTemplate" Value="{StaticResource ResourceKey=overWriteTempalte}" />
</DataTrigger>
</UserControl.Triggers>
</UserControl>
If Bindings are a problem Use Snoop to Detect binding errors

Change ListView.ItemTemplate on subelement change

let's say we have simple data class:
public class Ex {
public string Prop1 {...} // notify property
public string Prop2 {...} // notify property
}
and an ObservableCollection of objects of this class. I want to have this collection displayed in a ListView with seperated DataTemplated which is distinguished by Ex.Prop2 (if it is null or empty then template01 is used, otherwise template02). This property can be changed in runtime so simple "trick" with ListView.ItemTemplateSelector does not work :(
How to achieve this functionality? Is it possible to achieve it any other way than listening to NotifyPropertyChanged on each object of the collection and than changing manually the template?
Thanks for your help.
Below piece of code which I already have:
<ListView x:Name="lstTerms"
ItemsSource="{Binding Game.Words}"
HorizontalContentAlignment="Stretch"
Grid.IsSharedSizeScope="True">
<ListView.ItemContainerStyle>
<Style>
<Setter Property="Control.Padding" Value="0" />
</Style>
</ListView.ItemContainerStyle>
<!-- checks if element is null or its Prop2 is null or empty. If so, uses NullTemplate -->
<ListView.ItemTemplateSelector>
<local:MySelectTemplate
NormalTemplate="{StaticResource NormalItemTemplate}"
NullTemplate="{StaticResource NullItemTemplate}" />
</ListView.ItemTemplateSelector>
</ListView>
Instead of using a TemplateSelector, you can have a single DataTemplate containing two Grids, which switch visibility dependent on the property values.
Here is an example:
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid Background="LightBlue" Name="normalGrid">
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Prop1}" Value="{x:Null}">
<Setter Property="Grid.Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBlock Text="{Binding Prop1}"></TextBlock>
</Grid>
<Grid Background="Green" Name="nullGrid">
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=normalGrid, Path=Visibility}" Value="Visible">
<Setter Property="Grid.Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<TextBlock Text="{Binding Prop2}"></TextBlock>
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
Obviously you could replace the TextBlock elements with UserControls representing your two DataTemplates.
If you want, you can also remove the need for the bulky Styles by binding Grid.Visibility to a property (named, for example, IsVisible) on your ViewModel and using a VisibilityConverter.
I usually just use a ContentControl which changes its ContentTemplate based on a DataTrigger. DataTriggers respond to the value getting changed, while DataTemplateSelectors do not
<Style x:Key="SomeStyleKey" TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource DefaultTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Prop2}" Value="{x:Null}">
<Setter Property="ContentTemplate" Value="{StaticResource NullTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding Prop2}" Value="">
<Setter Property="ContentTemplate" Value="{StaticResource NullTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
...
<ListView.ItemTemplate>
<DataTemplate>
<ContentControl Style="{StaticResource SomeStyleKey}" />
</DataTemplate>
</ListView.ItemTemplate>
You could also use a Converter that returns String.IsNullOrEmpty(value) if you wanted a single DataTrigger

WPF: Setting Canvas color in a DataTrigger when bound value is null

I'm binding the Background property of a Canvas to some SelectedColorItem object like this:
<Canvas>
<Canvas.Style>
<Style TargetType="Canvas">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SelectedColorItem,Mode=OneWay,Converter={StaticResource isNullConverter}}"
Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Canvas.Style>
<Canvas.Background>
<SolidColorBrush Color="{Binding SelectedColorItem.MediaColor}" />
</Canvas.Background>
</Canvas>
It works - it displays the selected color.
What does not work it the DataTrigger which I wrote to indicate that the SelectedColorItem is null.
The simplest isNullValueConverter works either - it might be varified with a Label:
<Label Content="{Binding Path=SelectedColorItem,Mode=OneWay,Converter={StaticResource isNullConverter}}" />
It shows True, when SelectedColorItem is null and False otherwise.
Could somebody tell me what's wrong with the trigger?
P.S.
SelectedColorItem is an instance of a ColorItemClass which contains a MediaColor property:
public System.Windows.Media.Color MediaColor {get;set;}
The thing is that the value set on the element itself has higher "priority" when values from the style, meaning that values set inside the style (including triggers) cannot override locally set values.
That being set, you need to have both values on the same level, so just move the Background property binding inside the style:
<Canvas>
<Canvas.Style>
<Style TargetType="Canvas">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="{Binding SelectedColorItem.MediaColor}" />
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SelectedColorItem,Mode=OneWay,Converter={StaticResource isNullConverter}}"
Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Canvas.Style>
</Canvas>
You explicitly set the Background property. now the easiest way to achieve what you want is to set the bindings fallbackvalue to Red. Like:
<Canvas>
<Canvas.Background>
<SolidColorBrush Color="{Binding SelectedColorItem.MediaColor, FallbackValue=Red}" />
</Canvas.Background>
</Canvas>
or you could also use the same datatrigger with the value false to set the binding

Conditionals in WPF

I have the following question: I have a Boolean variable in a configuration file. If it is true I want a property in textbox control to be setup according to the value of that variable.
Try the solution above but it does not work. What am I doing wrong?
This is a fragment code:
bool isKeyboardAvtive = true; //read from configuration file
<Style x:Key="StylesTextBox" TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=isKeyboardActive}" Value="True">
<Setter Property="k:TouchScreenKeyboard.TouchScreenKeyboard" Value="True"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Path=isKyboardActive}" Value="False">
<Setter Property="k:TouchScreenKeyboard.TouchScreenKeyboard" Value="False"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<TextBox Style="{StaticResource StylesTextBox}" Margin="0,5" x:Name="txtUserName" Height="40" Width="150" />
IsKeyboardActive needs to be a public property of the DataContext for the binding to work. Also, you don't need a trigger there, just a binding :
k:TouchScreenKeyboard.TouchScreenKeyboard="{Binding IsKeyBoardActive}"
If you use the standard VS-generated settings, you can also bind to the settings directly :
xmlns:prop="clr-namespace:YourApplication.Properties"
...
k:TouchScreenKeyboard.TouchScreenKeyboard="{Binding IsKeyBoardActive, Source={x:Static prop:Settings.Default}}"
Or even better, using this markup extension :
xmlns:local="clr-namespace:YourApplication"
...
k:TouchScreenKeyboard.TouchScreenKeyboard="{local:SettingBinding IsKeyBoardActive}"

Resources