StaticResource and DynamicResource in XAML - c#

I am currently browsing WPF examples on this GitHub. This one is a simple application - it binds a textbox to a label. Whenever the textbox's text changes, the label is changed too via a data binding.
Now, I'm reading a StaticResource is evaluated the moment the XAML object is constructed and cannot be changed later, while a DynamicResource is evaluated each time it is accessed.
In this program, it would seem to me DynamicResources should be used, however only two StaticResources are present.
Here is what I'd deem the crucial code:
// ...
<Window.Resources>
<local:Person x:Key="MyDataSource" PersonName="Joe"/>
</Window.Resources>
// ...
<TextBox>
<TextBox.Text>
<Binding Source="{StaticResource MyDataSource}" Path="PersonName"
UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
<Label>The name you entered:</Label>
<TextBlock Text="{Binding Source={StaticResource MyDataSource}, Path=PersonName}"/>
How come it works with StaticResources?

As stated by Clemens,
The object referenced by StaticResource MyDataSource does not change, just the value of its PersonName property.

Related

Reusable DataTemplate (in Resources) using a convertor that needs a parameter

I have to display in a grid 12 columns (one for each months) with different data.
In order to correctly display the data, I use DataTemplates for every column and multibindings. The multibindings specify a converter which is parametrized with the index of the month.
<DataTemplate x:Key="CustomerForecastQuantityDataTemplate">
<TextBlock TextAlignment="Right">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource ForecastCustomerQuantityConverter}" **ConverterParameter="0"**>
<Binding RelativeSource="{RelativeSource AncestorType={x:Type view:BaseView}}" Path="DataContext.ForecastPeriods"/>
<Binding Path="ForecastQuantities"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
I am trying to make the xaml look a bit more elegant, and have one DataTemplate defined as a static resource, and use it everywhere, like this:
<forecast:ForecastCustomerMonthQuantityGridViewDataColumn
Header="{Binding RelativeSource={RelativeSource Self}, Path=MonthIndex}"
HeaderTextAlignment="Center"
HeaderTextWrapping="Wrap"
Width="60"
IsReadOnly="True"
MonthIndex="1"
CellTemplate="{StaticResource CustomerForecastQuantityDataTemplate}"/>
My question is, how can I make this DataTemplate take a diferrent parameter depending on the MonthIndex value of each ForecastCustomerMonthQuantityGridViewDataColumn
Thanks a lot, every suggestion is highly appreciated (it might be that I don't have a good understanding of the DataTemplate concept)
I can't think of a XAML-only way to get at "MonthIndex" from within the cell template (and on a side note ConverterParamter doesn't support binding). Off the top of my head, you could try something like this (I'm away from my PC so not able to try it myself):-
Add a third binding to your multi value converter, bound to the cell being templated, something like:
<binding RelativeSource="{RelativeSource AncestorType={x:Type DataGridCell}}" />
Your multi value converter now has access to the cell, which in turn exposes a Column property, which you can cast to ForecastCustomerMonthQuantityGridViewDataColumn to get at "MonthIndex".

Use a complex type for XAML Binding FallBackValue

I was searching for a way to bind a UI element to a complex type and have another complex type as FallBackValue. Unfortunately I didn't find anything on that topic. When I set FallBackValue to the name of a property or a type it is always displayed as a string :
<ContentControl Content="{Binding MyPathSelector, FallbackValue=model:PathSelector}"/>
In this case the string
model:PathSelector
will be displayed
Just in case you want to avoid tag syntax, or use the same fallback object for multiple bindings, you may use a resource:
<Window.Resources>
<model:PathSelector x:Key="FallbackPathSelector" />
</Window.Resources>
...
<ContentControl Content="{Binding MyPathSelector,
FallbackValue={StaticResource FallbackPathSelector}}"/>
I had to add the binding in tag syntax instead of attribute syntax and there I could reference the complex tape:
<ContentControl>
<ContentControl.Content>
<Binding Path="MyPathSelector">
<Binding.FallbackValue>
<model:PathSelector />
</Binding.FallbackValue>
</Binding>
</ContentControl.Content>
</ContentControl>

Why does this ComboBox ignore the DataTemplate when SelectedItem is a ContentControl?

In our application we have a screen design feature which is comprised of a custom ScreenDesignPanel and a Property Grid with a ComboBox at the top which points to the selected item on the ScreenDesignPanel. This allows the user to select the UIElement via the ComboBox or via the mouse to set its properties. We achieve this by binding the ItemsSource of the ComboBox to the ScreenDesignPanel's Children collection, then binding their SelectedItems together. This works great.
However, for whatever reason, if the SelectedItem is a ContentControl or a subclass like Button the ItemTemplate specified for the ComboBox is ignored for the 'selected item area' but it is applied when displaying the item in the dropdown list. If the SelectedItem is not a ContentControl, the template is used in both cases.
This also is seemingly specific to the ComboBox. If we use any other selector control: ListBox, ListView, ItemsControl... even third-party ComboBox controls... they all work as expected, properly applying the DataTemplate. ComboBox is doing something internally which no other control is doing.
Note: Below is an over-simplified example for illustrative purposes of the issue only. It is not how we're actually using it as described above.
Also of note: In the DataTemplate for the ComboBox.ItemTemplate, we are only using properties (i.e. Foreground in the example), and are not displaying the DataContext (i.e. the actual ContentControl) itself. This is important because again, the actual control already exists on the ScreenDesignPanel and therefore can't be used for display in the ComboBox's ItemTemplate as it would have two parents which isn't allowed. In other words, it is being used purely as data here.
One last thing... we have a working solution in our app, which was to wrap the Children before binding it to the ComboBox.ItemsSource. However, I'm still curious as to why the ComboBox behaves the way it does which is SPECIFICALLY what I'm asking. (In other words, I'm not looking for other solutions to this design. We already have a working one. I'm looking for clarity on the odd behavior of the ComboBox itself.)
On to the code!
In the first example below, note how the data template is applied to everything in the dropdown, but the selected item area only uses a template if the selected item is not a ContentControl.
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="I am the template" Foreground="{Binding Foreground}" />
</DataTemplate>
</ComboBox.ItemTemplate>
<!-- Four 'Data' items for example only -->
<TextBlock Text="I am a Red TextBox" Foreground="Red"/>
<ListBox Foreground="Purple">
<ListBoxItem>I am a Purple ListBox</ListBoxItem>
</ListBox>
<ContentControl Content="I am a Blue ContentControl" Foreground="Blue" />
<Button Content="I am a Button with Green text" Foreground="Green" />
</ComboBox>
This second example shows that it is completely acceptable and fully supported to use a UIElement as the content of a ContentPresenter and still use a DataTemplate (via ContentTemplate) so you can use it in a purely-data role, allowing the template itself to define the visual appearance without displaying the UIElement itself, which is used purely as data here.
<ContentPresenter>
<ContentPresenter.ContentTemplate>
<DataTemplate>
<TextBlock Text="I am the ContentTemplate" Foreground="{Binding Foreground}" />
</DataTemplate>
</ContentPresenter.ContentTemplate>
<ContentPresenter.Content>
<Button Content="I am the button" Foreground="Green" />
</ContentPresenter.Content>
</ContentPresenter>
Again, the issue is specific to a ComboBox. I want to find out why the data template isn't applied in that single case, and how to force it to be applied, if possible.
Of note, ComboBox does define SelectionBoxItemTemplate which is separate from the regular ItemTemplate but the rub is that is read-only so you can't set it. We really don't want to re-template the ComboBox as that can mess up proper theming.
Have you tried explicitly setting the DataTemplate to the ContentControl.ContentTemplate property?:
<UserControl.Resources>
<DataTemplate x:Key="DataTemplate">
<TextBlock Text="{Binding Content,
StringFormat='Displayed via template: {0}'}" />
</DataTemplate>
</UserControl.Resources>
...
<ContentControl Content="ContentControl"
ContentTemplate="{StaticResource DataTemplate}" />

How can I set the DataBinding to an arbitrary Property in XAML?

I have the following problem: I am designing an UserControl, a graded-color gauge. I have decieded to use the MVVM design patern, which turns out to be a good choice. However, I have the following problem. In the View XAML file, I try to convert a value to a color, using custtom converter, which needs 2 parameters. For this purpose, I use the MultiBinding:
<ItemsControl ItemsSource="{Binding Path=ViewData}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Height="2">
<Rectangle.Fill>
<MultiBinding Converter="{StaticResource colorConverter}">
<Binding Path="Value"/>
<Binding Source="{StaticResource Palette_ICOS}"/>
</MultiBinding>
</Rectangle.Fill>
</Rectangle>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The thing is, that I don't want to use
{StaticResource Palette_ISO1}
as a second parameter, but a Property, which is a direct property of the DataContext and is not a property of the ViewData collection member. I have tried several ways to accomplish this scenario, but without signifficant success.
As last, I have tried the following:
<Binding Path="CurrentPallete"/>
and the CurrentPallete looks like:
public Palette CurrentPalette
{
get { return _currentPalette; }
set
{
_currentPalette = value;
}
}
i.e. a Property in the Class, whose instance is set as a DataContext of the main control, which hosts the ItemControl. What I get is a
[0x00000001] = {DependencyProperty.UnsetValue}
value in the Debugger, when the corresponding convertor is invoked,which probably means, that the Property cannot be found. Can anyone point out what is the way to achieve the desired effect? Thanks a lot!
<Binding Path="DataContext.Palette_ICOS"
RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ItemsControl}" />
Have you tried
<Binding Path="Palette_ICOS"/>
If Palette_ICOS is a property in the current item's DataContext binding it should work.

Using a ValueConverter after a DataContext switch

I want to try out a small, custom ValueConverter in a class which after being instantiated (via default constructor, which only calls InitializeComponents() ), is given another DataContext, specifically an instance of a ViewModel.
Using a StaticResource within the Binding does not work at all (yields a NullReferenceException), since the DataContext has since then been changed (is not this anymore).
I've tried putting DataContext = this; before the InitializeComponents call, no change.
Should I be looking into this MarkupExtension gizmo (as described in this article) ?
I've also tried creating an instance of the custom Value Converter within the ViewModel (the current DataContext), doesn't help either.
I can provide additional details at all times. Thank you in advance !
I'm trying to display a ContextMenu within the TextBlock. The ContextMenu contains a sole MenuItem. The header of the MenuItem can be "Settings" for instance. The Children (rendered as MenuItems as well) of the said MenuItem stem from an Enum, hence the ItemsSource on the MenuItem.
Now all is getting displayed nicely, yet I am trying to make one of the Children (e.g. a member of the Enum) to be selected per default, since there is already a default Setting. Further background info can be found in my other question.
Edit :
...
<UserControl.Resources>
<Helpers:DisplayTypeToDefaultValueConverter x:Key="displayTypeConverter" />
</UserControl.Resources>
...
<TextBlock x:Name="InstructionLabel"
TextWrapping="Wrap" Text="{Binding Path=SelectedNodeText}"
Grid.RowSpan="1">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Settings" Name="SettingsPop"
DataContext="{Binding}"
ItemsSource="{Binding Source={StaticResource DisplayTypeValues}}"
IsCheckable="True"
Click="SettingsType_Click">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding}"/>
<Setter Property="IsChecked">
<Setter.Value>
<Binding Converter="{StaticResource displayTypeConverter}" />
</Setter.Value>
</Setter>
</Style>
</MenuItem.ItemContainerStyle>
</ContextMenu>
</TextBlock>
I've only now realized that it's the dreaded ContextMenu. That's the problem, isn't it ?
The DataContext inside the ItemContainerStyle is a member of the DisplayTypeValues collection. The only thing in the XAML you posted that will be affected by the DataContext of the UserControl changing is the InstructionLabel's text. Setting DataContext="{Binding}" as you're doing on the MenuItem is also redundant as the value will already be inherited from the parent ContextMenu.
It's not clear from your question or code what you're expecting from the DataContext or what you're trying to do with it.
Just several thoughts:
Are you sure you didn't miss setting binding path in <Binding Converter="{StaticResource displayTypeConverter}" />?
Did you check the StackTrace of the thrown exception and all it's InnerExceptions to see, whether there was something interesting?
Used an easier solution, as highlighted in my other related question.
Thank you for your input !

Categories