Static Binding Doesn't Update When Resource Changes

Static binding doesn't update when resource changes

First of all, your property is actually not a property, but a field. A minimal property declaration would look like this:

public static SolidColorBrush Property { get; set; }

Please note the name is starting with an uppercase letter, which is a widely accepted coding convention in C#.

Because you also want to have a change notification fired whenever the value of the property changes, you need to declare a property-changed event (which for non-static properties is usually done by implementing the INotifyPropertyChanged interface).

For static properties there is a new mechanism in WPF 4.5 (or 4.0?), where you can write a static property changed event and property declaration like this:

public static class AppStyle
{
public static event PropertyChangedEventHandler StaticPropertyChanged;

private static void OnStaticPropertyChanged(string propertyName)
{
StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
}

private static SolidColorBrush property = Brushes.Red; // backing field

public static SolidColorBrush Property
{
get { return property; }
set
{
property = value;
OnStaticPropertyChanged("Property");
}
}

public static void ChangeTheme()
{
Property = Brushes.Blue;
}
}

The binding to a static property would be written with the property path in parentheses:

Background="{Binding Path=(style:AppStyle.Property)}"          

How to update binding dynamically when source object changes?

You don't have to make the PageInformationProperty a dependency property just for this binding. Implement INotifyPropertyChanged in the code behind.

Also since you are actually binding to "UserControlPath", make sure that this property actually sends change notifications.

Two-way data binding with converter doesn't update source

With hints from several similar questions and almost answers here on SO, I have a working solution that preserves the binding. You can manually force the binding to update the source in a strategically placed event, such as LostFocus:

private void mycontrol_LostFocus(object sender, RoutedEventArgs e)
{
if (mycontrol.IsModified)
{
var binding = mycontrol.GetBindingExpression(MyControl.SampleProperty);
binding.UpdateSource();
}
}

How to update property binding when changing the culture of the application at runtime in WPF .net core 5

The Answer

App.xaml.cs

        public App()
{
CultureInfo CultureInformation = new CultureInfo("en-US");
CultureInformation.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy";
CultureInformation.DateTimeFormat.LongDatePattern = "ddd, dd/MM/yyyy";
CultureInfo.DefaultThreadCurrentCulture = CultureInformation;
CultureInfo.DefaultThreadCurrentUICulture = CultureInformation;
//
XmlLanguage language = XmlLanguage.GetLanguage(CultureInformation.IetfLanguageTag);
const BindingFlags kField = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly;
typeof(XmlLanguage).GetField("_equivalentCulture", kField).SetValue(language, CultureInformation);
typeof(XmlLanguage).GetField("_compatibleCulture", kField).SetValue(language, CultureInformation);
typeof(XmlLanguage).GetField("_specificCulture", kField).SetValue(language, CultureInformation);
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(language));
}

MainWindow.xaml.cs

        private void UpdateLanguage(string Language)
{
LanguageComboBox.SelectedValue = Properties.Settings.Default.Language = Language;
Properties.Settings.Default.Save();
//
ResourceDictionary Dictionary = new();
Dictionary.Source = new Uri(@$"..\Languages\{Language}.xaml", UriKind.Relative);
Resources.MergedDictionaries.Clear();
Resources.MergedDictionaries.Add(Dictionary);
//
if (Language == "العربية")
{
CultureInfo CultureInformation = CultureInfo.CreateSpecificCulture("ar-EG");
CultureInformation.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy";
CultureInformation.DateTimeFormat.LongDatePattern = "ddd, dd/MM/yyyy";
Thread.CurrentThread.CurrentCulture = CultureInformation;
Thread.CurrentThread.CurrentUICulture = CultureInformation;
//
XmlLanguage language = XmlLanguage.GetLanguage(CultureInformation.IetfLanguageTag);
const BindingFlags kField = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly;
typeof(XmlLanguage).GetField("_equivalentCulture", kField).SetValue(language, CultureInformation);
typeof(XmlLanguage).GetField("_compatibleCulture", kField).SetValue(language, CultureInformation);
typeof(XmlLanguage).GetField("_specificCulture", kField).SetValue(language, CultureInformation);
this.Language = language;
}
else if (Language == "English")
{
CultureInfo CultureInformation = CultureInfo.CreateSpecificCulture("en-US");
CultureInformation.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy";
CultureInformation.DateTimeFormat.LongDatePattern = "ddd, dd/MM/yyyy";
Thread.CurrentThread.CurrentCulture = CultureInformation;
Thread.CurrentThread.CurrentUICulture = CultureInformation;
//
XmlLanguage language = XmlLanguage.GetLanguage(CultureInformation.IetfLanguageTag);
const BindingFlags kField = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly;
typeof(XmlLanguage).GetField("_equivalentCulture", kField).SetValue(language, CultureInformation);
typeof(XmlLanguage).GetField("_compatibleCulture", kField).SetValue(language, CultureInformation);
typeof(XmlLanguage).GetField("_specificCulture", kField).SetValue(language, CultureInformation);
this.Language = language;
}
}

There is NO ConverterCulture class

    public class CultureAwareBinding : Binding
{
public CultureAwareBinding()
{
ConverterCulture = CultureInfo.CurrentCulture;
}
}

UserControl.xaml (Normal Binding)

            <TextBlock Grid.Row="5" FontSize="14" FontFamily="{StaticResource Segoe Semibold}" Foreground="{DynamicResource BackgroundBrush}">
<TextBlock Text="{Binding Path=StartTime, StringFormat={}{0:hh:mm tt}}"/>
<TextBlock Text="" FontSize="12" FontFamily="{StaticResource Segoe Icons}"/>
<TextBlock Text="{Binding Path=EndTime, StringFormat={}{0:hh:mm tt}}"/>
</TextBlock>

WPF Bind to sys:Boolean not updating

ResourceDictionary doesn't have any events that are raised when a resource is changed, and generally speaking, you don't use resources for this purpose. Usually, resources are used for things you want to create once and share, like styles, or things like images, brushes, etc. I would even go so far as to say you generally don't want to put something in your ResourceDictionary that could change at some point.

Recommendation: use INotifyPropertyChanged

WPF relies on two interfaces for change notification: for properties, you need to implement INotifyPropertyChanged. For collections, implement INotifyCollectionChanged (or more simply, just use an ObservableCollection<T>, which handles it all for you).

So you create a class:

public class CheckInternetModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;

private bool m_InternetConnected;
public bool InternetConnected
{
get { return m_InternetConnected; }
set
{
m_InternetConnected = value;
OnPropertyChanged();
}
}

protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

You can make this your control's DataContext and change your binding to {Binding InternetConnected}.

If you really want to use resources, you could add an instance of this class to your Resources:

<Window.Resources>
<vm:CheckInternetModel x:Key="CheckInternetModel"/>
</Window.Resources>

Your binding becomes...

<TextBlock Text="{Binding InternetConnected Source={StaticResource CheckInternetModel}, Converter={StaticResource BooleanToStringConverter}, StringFormat=Internet: {0}}" />

Your timer.Tickbecomes...

((CheckInternetModel)Application.Current.Resources["CheckInternetModel"]).InternetConnected = new CheckInternet().Status;

Workaround: manually invalidate the binding

If you insist on storing this as a boolean in the resource dictionary, the only solution left is to manually invalidate the binding using DependencyObject.InvalidateProperty. So you'll have to give your TextBlock a name and then from within your timer.Tick handler, add:

textBlock.InvalidateProperty(TextBlock.TextProperty);

That's not enough though. Your binding is to a StaticResource, so it will not query the ResourceDictionary to get the resource again. You'll have to change the binding to:

<TextBlock Text="{Binding Source={DynamicResource InternetConnected}, Converter={StaticResource BooleanToStringConverter}, StringFormat=Internet: {0}}" />

A DynamicResource forces the binding to look up the resource again each time it's requested, so when you invalidate the TextProperty, it'll retrieve InternetConnected from the dictionary again and it'll have the new value.

wpf using a static flag to update a property in a view model

The reason why your code doesn't work is that your static property has no change notification. So your triggers will never fire.

You may want to consider having your RefreshTitleBar property as a standard INotifyPropertyChanged property, buy put it into a singleton class. This way you will only have one instance of the class for your whole application and you'll get the change notifications you need.

You can implement change notification for a static property, but it's a little more difficult.

Binding static property and implementing INotifyPropertyChanged

I hope this helps.



Related Topics



Leave a reply



Submit