WPF - Freezable in a style of a button not inheriting DataContext - wpf

I am modeling an attached command pattern after the AttachedCommandBehavior library here. My button looks like this:
<Button>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="vms:Attached.Behaviors">
<Setter.Value>
<vms:Behaviors>
<vms:Behavior Event="Click"
Command="{Binding ClickCommand}" />
</vms:Behaviors>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
Everything works great, but when the setter on the Behavior is executed, the Command is null.
Behavior is a Freezable, and Behaviors is a FreezableCollection<Behavior>. It just doesn't seem to be inheriting the DataContext from the Button.
On the other hand, this works correctly:
<Button>
<vms:Attached.Behaviors>
<vms:Behavior Event="Click" Command="{Binding ClickCommand}" />
</vms:Attached.Behaviors>
</Button>
Unfortunately I can't do it this way, because I need to target generated ListViewItems using ItemContainerStyle.
Is there some way to get the DataContext in the Style?

The Attached Command Behavior library is the germ of the idea that became Blend Behaviors. The Blend Behaviors are much more powerful and standardized and so I recommend you switch to using them. But whether you are using Attached Command Behavior or Blend Behaviors, the problem is essential the same: they don't work as expected when trying to set them using a style. I've solved this problem for Blend Behaviors with full support for binding in this StackOverflow answer:
How to add a Blend Behavior in a Style Setter
Without testing it, I guess you have to move the ACB behavior to a resource marked with x:Shared="False" in order to get the binding to work.

I had the same problem, and using RelativeSource did the trick. I'll show you my before and after code...
Before: (This DIDN'T work)
<DataTemplate x:Key="MenuNodeWithChildrenTemplate">
<StackPanel Orientation="Horizontal"
behaviors:EventCommand.CommandToRun="{Binding OpenMenuItem}"
behaviors:EventCommand.EventName="MouseLeftButtonUp">
<Label Content="{Binding Title}"/>
<Label Content="{Binding Description}"/>
</StackPanel>
</DataTemplate>
After: (This DOES work)
<DataTemplate x:Key="MenuNodeWithChildrenTemplate">
<StackPanel Orientation="Horizontal"
behaviors:EventCommand.CommandToRun="{Binding Path=DataContext.OpenMenuItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemsControl}}}"
behaviors:EventCommand.EventName="MouseLeftButtonUp">
<Label Content="{Binding Title}"/>
<Label Content="{Binding Description}"/>
</StackPanel>
</DataTemplate>
You'll obviously have to tweak the parameters of the Relative Source to your specific situation. It seems that, for whatever reason, attached properties don't inherit the data context, so you have to tell if how to.

Related

WPF - Can one Bind a Label's property (Visibility) to the Label's Target?

So I have a number of XAML pages with various Controls, most of them with a TextBlock indicating the intended content. Like:
<TextBlock x:Name="txbCustomerName"
Text="Customer Name"/>
<TextBox x:Name="txtCustomerName"
Text="{Binding DataObject.CustomerName}"/>
I'm in the process of replacing the TextBlocks with Labels, which would look like this:
<Label x:Name="lblCustomerName"
Content="Customer Name"
Target="{Binding ElementName=txtCustomerName}"/>
<TextBox x:Name="txtCustomerName"
Text="{Binding DataObject.CustomerName}"/>
So far, so good. However, there are Controls that aren't always visible. Accordingly, the associated TextBlock follows suit:
<TextBlock x:Name="txbInvoiceAddressStreet"
Text="Street Name"
Visibility="{Binding DataObject.DifferentInvoiceAddress, Converter={StaticResource BoolToVisibility}}"/>
<TextBox x:Name="txtInvoiceAddressStreet"
Text="{Binding DataObject.InvoiceAddressStreet}"
Visibility="{Binding DataObject.DifferentInvoiceAddress, Converter={StaticResource BoolToVisibility}}"/>
I more or less hoped that the Label's Visibility would be automagically equal to that of its Target by default, but apparently I'll have to work for it. Which is fine, it's my job after all.
This first draft works great:
<Label x:Name="txbInvoiceAddressStreet"
Content="Street Name"
Target="{Binding ElementName=txtInvoiceAddressStreet}"
Visibility="{Binding Path=Visibility, ElementName=txtInvoiceAddressStreet}"/>
<TextBox x:Name="txtInvoiceAddressStreet"
Text="{Binding DataObject.InvoiceAddressStreet}"
Visibility="{Binding DataObject.DifferentInvoiceAddress, Converter={StaticResource BoolToVisibility}}"/>
You'll note that the Binding for my Label's Visibility is linked to the same element as Target instead of targeting the same data element as the TextBlock. I feel it concentrates relevant information in the TextBox instead of spreading it on both controls.
All this is working fine. Still, I can't help feeling that I might take it a step further if I found a way to apply that Binding to the TextBox's property directly through the Label's Target property instead of reusing the TextBox's name.
Like this, except it doesn't work because Source isn't a dependency property:
Visibility="{Binding Path=Visibility, Source={Binding Path=Target, RelativeSource={RelativeSource Self}}}"
As I said, this does not work. However, I hope it conveys a sense of what I'm trying for.
The ultimate step after that, of course, would be to move Visibility to the Labels' default style, so if there's a way to do that I'd like to know about it.
include Target property in binding path:
<Label Visibility="{Binding Path=Target.Visibility, RelativeSource={RelativeSource Self}}"/>
and it can be a part of Style:
<Style TargetType="Label">
<Setter Property="Visibility"
Value="{Binding Path=Target.Visibility, RelativeSource={RelativeSource Self}}"/>
</Style>

WPF - styling RadioButton as ToggleButton works, but shows error in VS

Working to create a top level menu in WPF, I used the trick of styling a RadioButton as a ToggleButton in order to get the "only one selected" effect. Like this:
<ItemsControl ItemsSource="{Binding ViewModels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding Key.Name}" GroupName="MenuButtonGroup"
Style="{StaticResource {x:Type ToggleButton}}" >
</RadioButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
It works beautifully, and does just what I expect. But Visual Studio registers it as an error.
The Style property is underlined in blue and the description given is The resource "{x:Type ToggleButton}" could not be resolved.
It all looks above board but having this sat on my error list in Visual Stuido is hugely irritating. Any idea how to resolve it?
EDIT: Just found this question -
The resource could not be resolved (VS 2010 RC)
Which suggests it's a VS error. Anyone confirm this, or know of a fix? Wherever the problem is, it's still really annoying!
You could move the style to a Resources collection e.g.
<Window.Resources>
<Style x:Key="MyStyle" TargetType="{x:Type RadioButton}" BasedOn="{StaticResource {x:Type ToggleButton}}" />
</Window.Resources>
and then reference that style:
<RadioButton Content="{Binding Key.Name}" GroupName="MenuButtonGroup"
Style="{StaticResource MyStyle}" />

Dynamically show/display wpf content

As the title says, I want to hide or display some content dynamically. I have two buttons working like radio buttons. Each button has to display a specific content, but the two contents must not be visible at the same time (it's a kind of form, and I'm working on two subclasses).
I've seen on SO two interesting things, and I wanted to mix them. Firstly, the use of ContentControl with a ToggleButton (http://stackoverflow.com/questions/7698560/how-to-bind-click-of-a-button-to-change-the-content-of-a-panel-grid-using-xaml). Secondly, using ToggleButtons as RadioButtons (http://stackoverflow.com/questions/2362641/how-to-get-a-group-of-toggle-buttons-to-act-like-radio-buttons-in-wpf second answer with 31)
So I decided to start with something like that :
<ContentControl>
<ContentControl.Template>
<ControlTemplate>
<WrapPanel HorizontalAlignment="Center">
<TextBlock Text="{x:Static Internationalization:Resources.MAINOPTIONSWINDOW_STATUSLINKSUPERVISOR}"/>
<RadioButton Style="{StaticResource {x:Type ToggleButton}}" Content="Alarm" GroupName="Trigger"/>
<RadioButton Style="{StaticResource {x:Type ToggleButton}}" Content="Event" GroupName="Trigger"/>
</WrapPanel>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
But Visual Studio underlines
{StaticResource {x:Type ToggleButton}}
Visually, my buttons appear like radio buttons instead of toggle buttons.
Visual says :
Impossible to resolve the resource "{StaticResource {x:Type
ToggleButton}}" (translated from french ;) )
Anyway this code works fine outside the ContentControl.
Yeah, first of all, fix your eventHandlers. Dont attach them on templates, tricky things can happen.
Second of all, you can modify your ContentControl.Resources, and put this to there:
<Style BasedOn="{StaticResource {x:Type ToggleButton}}"
TargetType="RadioButton"/>
And remove your own style definitions. You can even put that one level more up(parent of ContentControl). One more thing, you're doing this in code-behind, but I can assure you, true power of WPF comes from the fact that you dont need code-behind, atleast for logic like this.
Also the link you gave, it is using triggers. This is no biggie right now but you should know that it's not correct way. If you wanna switch between contents, check out ContentControl Content, DataTemplates, DataTemplateSelector. The idea is to have only one VIEW at memory, not hide things.
That can catch up, if youre doing the other way. It will eventually add more time to startup and memory usage.
In control templates you normally do not add event handlers.
Override the OnApplyTemplate and add the handler there.
I finally found another way
<RadioButton Content="Alarm" GroupName="TriggerStateInfo" Style="{StaticResource {x:Type ToggleButton}}"
Command="{x:Static StateInfoTemplateToAlarmCommand}"/>
<RadioButton Content="Event" GroupName="TriggerStateInfo" Style="{StaticResource {x:Type ToggleButton}}"
Command="{x:Static StateInfoTemplateToEventCommand}"/>
<ContentControl Content="{Binding Path=Trigger, ElementName=Window}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type States:AlarmTrigger}">
<ComboBox ItemsSource="{Binding Path=AlarmTriggersCollection}" />
</DataTemplate>
<DataTemplate DataType="{x:Type States:EventTrigger}">
<ComboBox ItemsSource="{Binding Path=EventTriggersCollection}" />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
With code behind changing type of Trigger

Using ItemsControl on a multi-leveled TreeView

My co-worker threatened to put me on TheDailyWTF today because of my property I wrote to be used to build a 3-tiered treeview with ItemsControl.
I bear you the footprint:
ObservableCollection<KeyValuePair<string, ObservableCollection<KeyValuePair<string, ObservableCollection<MyType>>>>>;
My goal was to create an ItemsControl that would use the Key as the header, and Value as the ItemsSource for 3 levels:
<Style x:Key="filterTreeStyle" TargetType="ItemsControl">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<controls:TreeViewItem IsExpanded="True">
<controls:TreeViewItem.Header>
<controlsToolkit:TreeViewItemCheckBox Content="{Binding Key}"/>
</controls:TreeViewItem.Header>
<ItemsControl ItemsSource="{Binding Value}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:TreeViewItem>
<controls:TreeViewItem.Header>
<controlsToolkit:TreeViewItemCheckBox Content="{Binding Key}"/>
</controls:TreeViewItem.Header>
<controlsToolkit:TreeViewItemCheckBox IsChecked="{Binding Enabled}" Content="{Binding FilterTypeText}"/>
</controls:TreeViewItem>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</controls:TreeViewItem>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Can anyone save me from the clutches of TheDailyWTF? What is a cleaner way to do this. Bonus if we can figure out a way to make the number of levels dynamic.
Uh, maybe I'm being dumb here, but since you want a TreeView... why not use a TreeView? You'll also need to use a HierarchicalDataTemplate instead of a vanilla DataTemplate: the content of the HDT becomes the Header, and the ItemsSource is used to create the child nodes. That will also take care of making the number of levels dynamic.
TreeView is built into WPF and is available in Silverlight as part of the SDK.

Silverlight MVVM ListBoxItem IsSelected

I have a collection of ViewModels bound to a ListBox. I am trying to bind the IsSelected properties of each together. In WPF it works by setting the style:
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}" />
</Style>
This does not work in Silverlight. How can I accomplish this?
In Silverlight, you are not able to create "global" styles, that is, styles that modify all controls of a certain type. Your style needs a key, and your control needs to reference it.
Also, TargetType simply needs the control type name. Silverlight does not support the x:Type extension.
ib.
Here's how i do it:
<ListBox.ItemTemplate>
<DataTemplate>
...
<CheckBox VerticalAlignment="Top" HorizontalAlignment="Left"
x:Name="CheckBox1" IsChecked="True" Grid.Row="0">
<inf:BindingHelper.Binding>
<inf:BindingProperties TargetProperty="Visibility" SourceProperty="IsSelected"
Converter="{StaticResource VisibilityConverter}"
RelativeSourceAncestorType="ListBoxItem" />
</inf:BindingHelper.Binding>
</CheckBox>
...
</DataTemplate>
</ListBox.ItemTemplate>
You need to do relative binding, which doesn't exist in Silverlight unfortunately...
BindingHelper is a helper class which overcomes this limitation (search for "relative binding in silverlight" to find it).

Resources