andrei.ciprian
andrei.ciprian

Reputation: 3025

WPF dynamically referenced application resources not updateable at runtime

I have this very small test app that explores object binding in WPF. I am binding to instances of this class MyData { public string Info { get; set; } } local class. I am exploring binding to a local object in xaml, setting data context at runtime and modifying resource instances at runtime. I am using both window and application resources to see their impact, as defined below:

App.xaml resources

<local:MyData x:Key="myAppData" Info="App resource information" />
<local:MyData x:Key="myAppEmptyData" />
<local:MyData x:Key="myAppBogusData" Info="-" />

Window.xaml resources

<local:MyData x:Key="myWindowData" Info="Window resource information" />
<local:MyData x:Key="myWindowEmptyData" />

The my[App|Window]EmptyData have a null Info member, and I am trying to modify it at runtime. I am doing this on window load:

(this.Resources["myWindowEmptyData"] as MyData).Info = "window resource info set on runtime";
(Application.Current.Resources["myAppEmptyData"] as MyData).Info = "app resource info set on runtime";
(Application.Current.Resources["myAppBogusData"] as MyData).Info = "app resource info set on runtime";
this.lblDataContextSetAtRuntime.DataContext = new MyData() { Info = "data context set at runtime" };

The code for the main window below. Green labels are expected behavior. Blue labels represent unmodified resources that one expects not to get modified (static + window). Red labels are unexpected behavior. See inline comments too.

MainWindow.xaml

    <Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" 
        DataContext="{StaticResource myWindowData}">
    <Grid.RowDefinitions>
      <RowDefinition Height="1*" /><RowDefinition Height="1*" /><RowDefinition Height="1*" /><RowDefinition Height="1*" /><RowDefinition Height="1*" /><RowDefinition Height="1*" /><RowDefinition Height="1*" /><RowDefinition Height="1*" />
    </Grid.RowDefinitions>   
    <!--this uses data context from the grid-->
    <Label Content="{Binding Path=Info}" Background="Green" Grid.Row="0" />
    <!--normal to not be updateable since window resources-->
    <Label DataContext="{StaticResource myWindowEmptyData}" Content="{Binding Path=Info}" Background="CadetBlue" Grid.Row="1" x:Name="lblStaticWindowResource" />
    <Label DataContext="{DynamicResource myWindowEmptyData}" Content="{Binding Path=Info}" Background="CadetBlue" Grid.Row="2" x:Name="lblDynamicWindowResource" />
    <!--data context set at runtime-->
    <Label Content="{Binding Path=Info}" Grid.Row="3" Background="Green" x:Name="lblDataContextSetAtRuntime" />
    <!--uses data context from app-->
    <Label DataContext="{StaticResource myAppData}" Content="{Binding Path=Info}" Background="Green" Grid.Row="4" x:Name="lblAppData" />
    <!--static app resource not modifiable-->
    <Label DataContext="{StaticResource myAppEmptyData}" Content="{Binding Path=Info}" Background="CadetBlue" Grid.Row="5" x:Name="lblStaticAppResource"/>
    <!--DYNAMIC APP RESOURCE SHOULD GET MODIFIED-->
    <Label DataContext="{DynamicResource myAppEmptyData}" Content="{Binding Path=Info}" Background="Red" Grid.Row="6" x:Name="lblDynamicEmptyAppResource" />
    <Label DataContext="{DynamicResource myAppBogusData}" Content="{Binding Path=Info}" Background="Red" Grid.Row="7" x:Name="lblDynamicBogusAppResource" />
  </Grid>

Even though in the debugger window one notices the resources are modified enter image description here

, at runtime there's no content for the blue and red labels. I thought there was an issue with the resources with null property, bit not even myAppBogusData gets updated.

enter image description here

Upvotes: 0

Views: 1387

Answers (1)

Adi Lester
Adi Lester

Reputation: 25211

MyData must implement INotifyPropertyChanged in order for the Info property to update via binding.

class MyData : INotifyPropertyChanged
{
    private string info;
    public string Info
    {
        get { return info; }
        set
        {
            if (info != value)
            {
                info = value;
                RaisePropertyChanged("Info");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Other than that, you don't have to use a DynamicResource - a StaticResource would suffice. You would only need to use a DynamicResource if you have a need to change the MyData instance entirely. For example:

Application.Current.Resources["myAppBogusData"] = new MyData() { Info = "New data" };

If you're just changing properties on a given instance, you can use StaticResource.

Upvotes: 1

Related Questions