I have one int value in my Vmodel :
public int MaxTagCount => URLsCount.Max(tag => tag.Count);
And I need to connect this MaxTagCount with Trigger :
<DataTrigger Binding="{Binding Count}" Value="1149">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="Green"/>
</DataTrigger>
How can I replace "1149" to MaxTagCount ?
If i understand you correctly, you are looking for a way to bind the Value of the DataTrigger to your MaxTagCount property, which isn't possible due to the fact that Value isn't a dependency property.
The most common workaround that is to pass both the MaxTagCount property and the Count property to a MultiValueConverter, the converter will compare those two values and return true or false. The role of the DataTrigger now will be to check the value returned by the converter so:
First, define a basic converter that compares two values like so:
public class CompareValuesConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values?[0].Equals(values[1]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Second, update your DataTrigger to check the returned value of the converter, and pass your values to the converter, and set your style accordingly:
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding >
<MultiBinding.Converter>
<local:CompareValuesConverter/>
</MultiBinding.Converter>
<Binding Path="Count" />
<Binding Path="DataContext.MaxTagCount" ElementName="Main"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="Green"/>
</DataTrigger>
Notice that i am using an ElementName binding to get the MaxTagCount value since it is (most likely) defined on the global UI DataContext (in this case the main Window is Named Main), you could also use a RelativeSource binding.
As already explained by #Elhamer, you can't bind to the Value property of a DataTrigger because it is not a dependency property.
As an alternative to using a multi converter, you could just add another property to your view model that returns a bool that indicates whether the Count and MaxTagCount properties are equal:
public bool IsMax => Count == MaxCount;
...and bind to this one:
<DataTrigger Binding="{Binding IsMax}" Value="True">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="Green"/>
</DataTrigger>
After all, a view model is nothing but a model for the view and this kind of logic makes perfect sense to implement there.
Related
Just out of interest....
In case I have a ViewModel with an uninitialized string, which is bound to a Textbox, I can use TargetNullValue to display a default value.
However, I was wondering if I can use the same value to update the string in case it is null?
Basically instead of
set
{
if(value != null) text = value;
else value = "defaultstring";
OnPropertyChanged();
}
just do the same thing from the databinding using TargetNullValue.
You can manipulate the getter as well as the data binding will use the get():
private string text;
public string Text
{
get
{
if (text== null)
return "default value";
else
return this.text;
}
set { this.text= value; }
}
However, if you want to do it in Pure XAML you can use a DataTrigger for this:
<TextBlock Text="{Binding MyText}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock }">
<Style.Triggers>
<DataTrigger Binding="{Binding MyText}" Value="{x:Null}">
<Setter Property="Text" Value="DefaultValue"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
I'm using a ICollectionView as ItemsSource for a DataGrid. Using a Style, I'm highlighting the cells of my DataGrid that contain a specific SearchString. Simultaneously I'd like to filter the DataGrid to display those lines that contain at least one cell that matches my search.
For the ICollectionView.Filter to effect the displayed data in my DataGrid, I need to call ICollectionView.Refresh(), which apparently clears the style of the cells, because the whole data is reloaded and displayed fresh.
What would be a way to preserve (or re-apply) the style of the cells after a ICollectionView.Refresh()?
Here is the code I'm using to style the cells:
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource ContainsStringToBooleanConverter}">
<Binding Path="Content" RelativeSource="{RelativeSource Self}"/>
<Binding Path="DataContext.SearchString" RelativeSource="{RelativeSource AncestorType={x:Type Window}}"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Background" Value="LightGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
Here is how I'm filtering the ICollectionView:
ItemView = CollectionViewSource.GetDefaultView(myItemChildren);
ItemView.Filter = ItemViewFilter;
...
private bool ItemViewFilter(object o)
{
if (String.IsNullOrWhiteSpace(SearchString)) return true;
var item = (o as Item);
if (item == null) return true;
bool result = (item.ItemText.ToLower().Contains(SearchString.ToLower()));
return result;
}
Refresh() is called every time SearchString is updated:
public string SearchString
{
get
{
return mySearchString;
}
set
{
mySearchString = value;
ItemView.Refresh();
OnPropertyChanged("SearchString");
}
}
Edit:
After mm8's comment I took a look at my IMultiValueConverter. It seems when the converter is called after a Refresh, none of the cells contain any text. Here is my converter:
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length != 2) return false;
if (values[0] == null || values[1] == null) return false;
string text = "";
if (values[0].GetType() == typeof(TextBlock))
{
// The TextBlock never contains any Text after a Refresh
text = ((TextBlock)values[0]).Text;
}
string search = values[1] as string;
if (text == null || search == null) return false;
if (String.IsNullOrWhiteSpace(text) || String.IsNullOrWhiteSpace(search)) return false;
return text.ToLower().Contains(search.ToLower());
}
I switched from my "filter & style" approach for highlighting cells and showing only the rows containing such highlighted cells to an style-only approach.
I added an additional DataGrid.RowStyle to hide the rows not containing my search string:
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource ContainsStringToBooleanConverter}">
<Binding Path="." RelativeSource="{RelativeSource Self}"/>
<Binding Path="DataContext.SearchString" RelativeSource="{RelativeSource AncestorType={x:Type Window}}"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
This works pretty well and I feel like it might even be a little faster than the filter approach.
I don't know if need to combine DataTrigger & Trigger, if there's better way please tell me.
My goal is, to create a menu(with icons), icons will change while meet hover or selected event.
Here's an enum define all menu types:
public enum PageTypes:byte
{
NotSet = 0,
HomePage = 1,
ShopPage = 2,
AboutPage = 3
}
Then I created a MenuItemModel represent each menu item:
public class MenuItemModel : INotifyPropertyChanged
{
private PageTypes _menuItemType = PageTypes.NotSet;
public PageTypes MenuItemType { get { return _menuItemType; } set { if (value != _menuItemType) { _menuItemType = value; RaisePropertyChanged(() => MenuItemType); } } }
private bool _isSelected = false;
public bool IsSelected { get { return _isSelected; } set { if (value != _isSelected) { _isSelected = value; RaisePropertyChanged(() => IsSelected); } } }
}
Ok, then I begin to create UI.
<!-- MenuItem Template -->
<DataTemplate x:Key="MenuTemplate">
<Button Command="{Binding ClickCommand}" CommandParameter="{Binding}">
<Image>
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source" Value="/Image/Home_normal.png"/>
<Style.Triggers>
<DataTrigger Binding="{Binding MenuItemType}" Value="ShopPage">
<Setter Property="Source" Value="/Image/Shop_normal.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding MenuItemType}" Value="AboutPage">
<Setter Property="Source" Value="/Image/About_normal.png"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Button>
</DataTemplate>
till now everything is very easy, but when I try to make mouseOver and Selected effect, problem comes.
for example, if mouse over home_normal.png, it should change to home_hover.png, if IsSelected property is TRUE, image should be ignore hover trigger then use home_selected.png. But there's 3 image, how do I know what image should change?
<!-- MenuItem Template -->
<DataTemplate x:Key="MenuTemplate">
<Button Command="{Binding ClickCommand}" CommandParameter="{Binding}">
<Image>
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source" Value="/Image/Home_normal.png"/>
<Style.Triggers>
<DataTrigger Binding="{Binding MenuItemType}" Value="ShopPage">
<Setter Property="Source" Value="/Image/Shop_normal.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding MenuItemType}" Value="AboutPage">
<Setter Property="Source" Value="/Image/About_normal.png"/>
</DataTrigger>
<!-- MY PLAN -->
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Source" Value="?_hover.png"/>
</Trigger>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Source" Value="?_selected.png"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Button>
</DataTemplate>
If you can see the question mark in "MY PLAN" comment, that would be my question: what should I do in the Value field?
You can use MultiDataTrigger like this. But you should add same 3 triggers for all types of pages. Note that next trigger overrides below and conditions works like logical AND.
<p:Style.Triggers xmlns:p="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<DataTrigger Binding="{Binding MenuItemType}" Value="ShopPage">
<Setter Property="Source" Value="/Image/Shop_normal.png"/>
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding MenuItemType}" Value="ShopPage" />
<Condition Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=IsMouseOver}" Value="true" />
</MultiDataTrigger.Conditions>
<Setter Property="Source" Value="/Image/Shop_MouseOver.png" />
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding MenuItemType}" Value="ShopPage" />
<Condition Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=IsSelected}" Value="true" />
</MultiDataTrigger.Conditions>
<Setter Property="Source" Value="/Image/Shop_IsSelected.png" />
</MultiDataTrigger>
</p:Style.Triggers>
In my opinion, the answer you've already received and accepted is a good one. It's entirely XAML-based, which seems to be a primary goal in your scenario, and it should work very well. That said, the XAML-only solution is fairly verbose and involves a lot of redundant code. This is already seen in the scenario above where you have two buttons types, each with three possible states. And it will only get worse as you add button types and states.
If you are willing to do a little code-behind, I think you can accomplish the same effect but with a lot less redundancy.
Specifically, if you use <MultiBinding>, you can bind the relevant properties to a collection that can be used to look up the correct image source. In order for me to accomplish this, I needed to create a couple of small container types to store the lookup data, and of course the IMultiValueConverter implementation to use them:
Container types:
[ContentProperty("Elements")]
class BitmapImageArray
{
private readonly List<ButtonImageStates> _elements = new List<ButtonImageStates>();
public List<ButtonImageStates> Elements
{
get { return _elements; }
}
}
class ButtonImageStates
{
public string Key { get; set; }
public BitmapImage[] StateImages { get; set; }
}
Converter:
class OrderedFlagConverter : IMultiValueConverter
{
public object Convert(object[] values,
Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
BitmapImageArray imageData = (BitmapImageArray)parameter;
string type = (string)values[0];
foreach (ButtonImageStates buttonStates in imageData.Elements)
{
if (buttonStates.Key == type)
{
int index = 1;
while (index < values.Length)
{
if ((bool)values[index])
{
break;
}
index++;
}
return buttonStates.StateImages[index - 1];
}
}
return DependencyProperty.UnsetValue;
}
public object[] ConvertBack(object value,
Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
In your example, using the above might look something like this:
<DataTemplate x:Key="MenuTemplate">
<Button Command="{Binding ClickCommand}" CommandParameter="{Binding}">
<Image>
<Image.Source>
<MultiBinding>
<MultiBinding.Converter>
<l:OrderedFlagConverter/>
</MultiBinding.Converter>
<MultiBinding.ConverterParameter>
<l:BitmapImageArray>
<l:ButtonImageStates Key="ShopPage">
<l:ButtonImageStates.StateImages>
<x:Array Type="{x:Type BitmapImage}">
<BitmapImage UriSource="/Image/Shop_selected.png"/>
<BitmapImage UriSource="/Image/Shop_hover.png"/>
<BitmapImage UriSource="/Image/Shop_normal.png"/>
</x:Array>
</l:ButtonImageStates.StateImages>
</l:ButtonImageStates>
<l:ButtonImageStates Key="AboutPage">
<l:ButtonImageStates.StateImages>
<x:Array Type="{x:Type BitmapImage}">
<BitmapImage UriSource="/Image/About_selected.png"/>
<BitmapImage UriSource="/Image/About_hover.png"/>
<BitmapImage UriSource="/Image/About_normal.png"/>
</x:Array>
</l:ButtonImageStates.StateImages>
</l:ButtonImageStates>
</l:BitmapImageArray>
</MultiBinding.ConverterParameter>
<Binding Path="ButtonType"/>
<Binding Path="IsMouseOver" RelativeSource="{RelativeSource Self}"/>
<Binding Path="IsSelected"/>
</MultiBinding>
</Image.Source>
</Image>
</Button>
</DataTemplate>
The converter takes, as input, bindings to the properties that affect the visual state of the button. The first bound value is simply the type of the button; this is used to look up the correct array of button states for the button. The remaining bound values (you can have arbitrarily many in this approach) are flags that are searched; the images are stored in the same order as the flags, with one additional "default" image at the end (i.e. if no flags are set, the default image is returned).
In this way, adding new button types involves only adding a new ButtonImageStates object, specifying the correct key for that button type, and adding new button states involves only adding a single line to each button type's list: the BitmapImage reference that corresponds to the image for that state for that button type.
Doing it this way drastically cuts down on the amount of code one has to add as new button types and states are needed: a given button type need be mentioned in the XAML only once, and likewise each triggering property is mentioned only once. A XAML-only approach will require a lot of duplicated boilerplate, and the actual image file references will be scattered throughout the style declaration.
Here is a simple demo of the basic technique. Lacking a good MCVE to start with, I didn't want to waste time re-creating parts of the code that weren't strictly necessary for the purposes of a demonstration:
I only bothered to create four state images, and of course only wrote code to deal with four possible states: two each for two different button types.
I also didn't bother with putting this in a menu; I'm just using a plain ItemsControl to present the buttons.
Naturally, the view model is a degenerate class; I didn't bother with property-change notification, since it's not needed here. The example still works if you include that though.
Here are the images used in the example (I'm a programmer, not an artist…I considered not even bothering with image content, since that's also not strictly required to demonstrate the basic technique, but figured I could handle four basic images :) ):
These are added to the project in a "Resources" folder, with the Build Action set to Resource.
XAML:
<Window x:Class="TestSO34193266MultiTriggerBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:TestSO34193266MultiTriggerBinding"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<l:OrderedFlagConverter x:Key="orderedFlagConverter1"/>
<BitmapImage x:Key="bitmapRedNormal"
UriSource="pack://application:,,,/Resources/red_normal.png"/>
<BitmapImage x:Key="bitmapRedHover"
UriSource="pack://application:,,,/Resources/red_hover.png"/>
<BitmapImage x:Key="bitmapGreenNormal"
UriSource="pack://application:,,,/Resources/green_normal.png"/>
<BitmapImage x:Key="bitmapGreenHover"
UriSource="pack://application:,,,/Resources/green_hover.png"/>
<l:ViewModel x:Key="redViewModel" ButtonType="Red"/>
<l:ViewModel x:Key="greenViewModel" ButtonType="Green"/>
<x:Array x:Key="items" Type="{x:Type l:ViewModel}">
<StaticResource ResourceKey="redViewModel"/>
<StaticResource ResourceKey="greenViewModel"/>
</x:Array>
<x:Array x:Key="redButtonStates" Type="{x:Type BitmapImage}">
<StaticResource ResourceKey="bitmapRedHover"/>
<StaticResource ResourceKey="bitmapRedNormal"/>
</x:Array>
<x:Array x:Key="greenButtonStates" Type="{x:Type BitmapImage}">
<StaticResource ResourceKey="bitmapGreenHover"/>
<StaticResource ResourceKey="bitmapGreenNormal"/>
</x:Array>
<l:BitmapImageArray x:Key="allButtonStates">
<l:ButtonImageStates Key="Red" StateImages="{StaticResource redButtonStates}"/>
<l:ButtonImageStates Key="Green" StateImages="{StaticResource greenButtonStates}"/>
</l:BitmapImageArray>
<ItemsPanelTemplate x:Key="panelTemplate">
<StackPanel IsItemsHost="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
<DataTemplate x:Key="template" DataType="l:ViewModel">
<Button>
<Image Stretch="None">
<Image.Source>
<MultiBinding Converter="{StaticResource orderedFlagConverter1}"
ConverterParameter="{StaticResource allButtonStates}">
<Binding Path="ButtonType"/>
<Binding Path="IsMouseOver" RelativeSource="{RelativeSource Self}"/>
</MultiBinding>
</Image.Source>
</Image>
</Button>
</DataTemplate>
<!-- explicit namespace only for the benefit of Stack Overflow formatting -->
<p:Style TargetType="ItemsControl"
xmlns:p="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Setter Property="ItemsSource" Value="{StaticResource items}"/>
<Setter Property="ItemsPanel" Value="{StaticResource panelTemplate}"/>
</p:Style>
</Window.Resources>
<StackPanel>
<ItemsControl ItemTemplate="{StaticResource template}"/>
</StackPanel>
</Window>
C#:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
class ViewModel
{
public string ButtonType { get; set; }
}
class OrderedFlagConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
BitmapImageArray imageData = (BitmapImageArray)parameter;
string type = (string)values[0];
foreach (ButtonImageStates buttonStates in imageData.Elements)
{
if (buttonStates.Key == type)
{
int index = 1;
while (index < values.Length)
{
if ((bool)values[index])
{
break;
}
index++;
}
return buttonStates.StateImages[index - 1];
}
}
return DependencyProperty.UnsetValue;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
[ContentProperty("Elements")]
class BitmapImageArray
{
private readonly List<ButtonImageStates> _elements = new List<ButtonImageStates>();
public List<ButtonImageStates> Elements
{
get { return _elements; }
}
}
class ButtonImageStates
{
public string Key { get; set; }
public BitmapImage[] StateImages { get; set; }
}
One minor note: for some reason I get in the XAML editor the following error message on the <Window> element declaration:
Collection property 'TestSO34193266MultiTriggerBinding.ButtonImageStates'.'StateImages' is null.
I've clearly failed to jump through some hoop the XAML editor wants me to clear with respect to the declaration and/or implementation of ButtonImageStates, but what that is I don't know. The code compiles and runs just fine, so I haven't bothered to try to figure that part out. It may well be the case that there's a better way to represent the map of button state images, but this way works and other than the spurious error seems fine to me.
I've got a custom glass-like button that, when pressed, shows a black background. That's fine, but I was hoping for something more. I want to, rather than show the background as black, show the background as something derived from the actual background color of the button.
Is it possible, from the XAML, to call a function, pass the background color, and use the return value in the trigger?
This is what I have now
<Trigger Property="IsPressed" Value="True">
<Setter Property="Visibility" TargetName="Glow" Value="Hidden"/>
<Setter Property="Opacity" TargetName="Shine" Value="0.4"/>
<Setter Property="Background" TargetName="Border" Value="#CC000000"/>
</Trigger>
And this is what I would like -
<Trigger Property="IsPressed" Value="True">
<Setter Property="Visibility" TargetName="Glow" Value="Hidden"/>
<Setter Property="Opacity" TargetName="Shine" Value="0.4"/>
<Setter Property="Background" TargetName="Border" Value="{GetColor(Border.Background)}"/>
</Trigger>
How can I make that happen?
EDIT
It was suggested I should use a ValueConverter : I have since implemented one here :
public class PRBGConverter : IValueConverter {
public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture ) {
Console.WriteLine( "Clicked!" );
if ( value is SolidColorBrush ) {
SolidColorBrush SCB = value as SolidColorBrush;
SCB.Color = Color.FromArgb( 0xCC, SCB.Color.R, SCB.Color.G, SCB.Color.B );
return SCB;
} else if ( value is GradientBrush ) {
GradientBrush GB = value as GradientBrush;
foreach ( GradientStop GS in GB.GradientStops )
GS.Color = Color.FromArgb( 0xCC, GS.Color.R, GS.Color.G, GS.Color.B );
return GB;
} else return value;
}
public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture ) {
Console.WriteLine( "UnClicked!" );
if ( value is SolidColorBrush ) {
SolidColorBrush SCB = value as SolidColorBrush;
SCB.Color = Color.FromArgb( 0x99, SCB.Color.R, SCB.Color.G, SCB.Color.B );
return SCB;
} else if ( value is GradientBrush ) {
GradientBrush GB = value as GradientBrush;
foreach ( GradientStop GS in GB.GradientStops )
GS.Color = Color.FromArgb( 0x99, GS.Color.R, GS.Color.G, GS.Color.B );
return GB;
} else
return value;
}
}
Unfortunately, as evidenced by the console debug output, the ConvertBack method is never called, resulting in a pressed button background remaining opaque - how do I correct this?
XAML -
<Trigger Property="IsPressed" Value="True">
<Setter Property="Visibility" TargetName="Glow" Value="Hidden"/>
<Setter Property="Opacity" TargetName="Shine" Value="0.4"/>
<Setter Property="Background" TargetName="Border">
<Setter.Value>
<Binding ElementName="Border" Path="Background" Converter="{StaticResource BGConverter}" UpdateSourceTrigger="PropertyChanged"/>
</Setter.Value>
</Setter>
</Trigger>
I am wondering if anyone could explain me the difference between
binding a selected value of a Collection to a comboBox.
Or Binding the value to a Button Content.
Like that
<ComboBox x:Name="_culturedTitleViewModelSelector" Visibility="Hidden" Style="{StaticResource ResourceKey=_culturedTitleViewModelSelectorStyle}"
ItemsSource="{Binding Path=AvailableCultures, Source={x:Static Localized:ResourcesManager.Current}}"
SelectedValue="{Binding Path=CurrentCulture, Source={x:Static Localized:ResourcesManager.Current}}"
<Button x:Name="LanguageBtn" Content="{Binding Path=CurrentCulture, Source={x:StaticLocalized:ResourcesManager.Current}}"
The issue is If i Don't use the ComboBox up there, the DependencyProperty I Have in another class is not being called.
But if I Use the comboBox everything works...
Altought the comboBox doesnt do anything it's just a "workarround"
In my CS code when i CLick on my button I DO that :
ResourcesManager.Current.SwitchToNextCulture();
//We use a dummy comboBox to make sure the LanguageBehavior Property is being notified.
_culturedTitleViewModelSelector.SelectedItem = ResourcesManager.Current.CurrentCulture;
And if I Dont set the SelectedItem of the combobox to another culture. My languageBehavior class is not notified.
:
public class LanguageBehavior
{
public static DependencyProperty LanguageProperty =
DependencyProperty.RegisterAttached("Language",
typeof(string),
typeof(LanguageBehavior),
new UIPropertyMetadata(OnLanguageChanged));
public static void SetLanguage(FrameworkElement target, string value)
{
target.SetValue(LanguageProperty, value);
}
public static string GetLanguage(FrameworkElement target)
{
return (string)target.GetValue(LanguageProperty);
}
private static void OnLanguageChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
var element = target as FrameworkElement;
if (e.NewValue!=null)
element.Language = XmlLanguage.GetLanguage(e.NewValue.ToString());
}
}
I'd expect ComboBox Content to work the same as Button Content.
In my Generic.Xaml i do that :
<Style TargetType="{x:Type TextBlock}" x:Key="_textBlockLanguageProperty">
<Setter Property="WpfServices:LanguageBehavior.Language" Value="{Binding Path=CurrentCulture, Source={x:Static Localized:ResourcesManager.Current}}"
/>
</Style>
And that is CurrentCulture
public CultureInfo CurrentCulture
{
get { return CultureProvider.Current; }
set
{
if (value != CultureProvider.Current)
{
CultureProvider.Current = value;
OnCultureChanged();
}
}
}
Current :
public static ResourcesManager Current
{
get
{
if (_resourcesManager == null)
{
var cultureProvider = new BaseCultureProvider();
_resourcesManager = new ResourcesManager(cultureProvider);
_resourcesManager.Init();
}
return _resourcesManager;
}
}
EDIT :
My _culturedTitelViewModelSelectorStyle is
<Style TargetType="{x:Type ComboBox}" x:Key="_culturedTitleViewModelSelectorStyle">
<Setter Property="DisplayMemberPath" Value="DisplayName" />
<Setter Property="SelectedValuePath" Value="." />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="MaxHeight" Value="40" />
<Setter Property="FontSize" Value="20" />
<Setter Property="Margin" Value="5" />
<Setter Property="SelectedIndex" Value="0" />
<Setter Property="IsSynchronizedWithCurrentItem" Value="True" />
</Style>
In the ComboBox you are binding the SelectedValue to a specific culture. This will select that culture from the list of available cultures, and therefor, trigger a set on the CurrentCulture property.
The Content property of a Button is merely displaying something to the user, it is not doing any assigning. It reads the property value and then displays it. That is why you need to manually change the Culture in the Click event to get it to do anything.
If you want the user to be able to select a value from a list of available values, a ComboBox or ListBox is the way to go. A Button is for triggering a specific action, not for selecting from a list.