WPF: CommandParameters for input bindings - wpf

One of the things I really like with WPF is the extent to which my views can be built declaratively, ie. using XAML rather than code-behind.
Now I'm really stumped by InputBindings, because their CommandParameters don't accept bindings. I imagine my case is pretty generic and straightforward, but I cannot see how I can do it without resorting to code-behind. Consider:
<ListBox Name="casingsListBox" ItemsSource="{Binding Path=Casings}" SelectedValuePath="Id">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Title}"/>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.InputBindings>
<!-- Doesn't work: -->
<MouseBinding Gesture="LeftDoubleClick"
Command="ApplicationCommands.Open"
CommandParameter="{Binding RelativeSource={RelativeSource Self} Path=SelectedValue}"/>
</ListBox.InputBindings>
</ListBox>
This will not work, since the binding expression for the MouseBinding's CommandParameter is illegal.
I ask myself: what's the point of adding a mouse-click gesture to a listbox if I cannot get to the selected value?
This can be easily solved using a code-behind event handler, of course, or by having the command consumer extract the id from the command source, but there are several reasons why this is undesirable. Apart from the fact that droves of code-behind code defeats (some of) the purpose of WPF in the first place, it makes the UI designers working in Expression Blend less empowered. And dammit, my command parameter shall be an id, not some UI element!!
Subjective: Having browsed SO for a while, I'm struck by the amount of code I see in the WPF-related questions. I get the feeling we developers stick to our old habits and happily hack away in the code-behind file rather than trying to utilize the fresh take on UI building that WPF is supposed to represent. What do you think?
But most importantly: can anyone show me a code-free workaround for this seemingly trivial problem? Preferably without terrible hacks like this one.

I wrote a markup extension that allows an InputBinding's Command to be databound :
<KeyBinding Modifiers="Control" Key="E" Command="{input:CommandBinding EditCommand}"/>
Your situation is slightly different since you want to bind the CommandParameter, but you can probably adapt my code to fit your case. Note that this code uses private reflection, which only works in full-trust, and can be broken in later versions of WPF (actually it is broken in WPF 4.0... I can post a modified version if you need it).
Another option is to use the CommandReference class that can be found in the MVVM toolkit :
<Window.Resources>
<c:CommandReference x:Key="EditCommandReference" Command="{Binding EditCommand}"/>
</Window.Resources>
...
<KeyBinding Modifiers="Control" Key="E" Command="{StaticResource EditCommandReference}"/>
Again, this is for binding the Command property, but can probably be adapted to bind the CommandParameter...

The new way to solve this problem is by using Expression Triggers / Actions which allow you to set up keyboard shortcuts on arbitrary controls that do custom actions (like firing a Command).

Related

Can I bind to an ancestor DataTemplate in Silverlight?

I'm having trouble pulling off a binding that I believe should be valid in Silverlight 5:
<Controls:GraphLayout>
<Controls:GraphLayout.VertexTemplate>
<DataTemplate>
<TextBox ... />
</DataTemplate>
</Controls:GraphLayout.VertexTemplate>
<Controls:GraphLayout.SubgraphTemplate>
<DataTemplate>
<Controls:GraphLayout VertexTemplate="{Binding VertexTemplate, RelativeSource={RelativeSource AncestorType=Controls:GraphLayout}}"/>
</DataTemplate>
</Controls:GraphLayout.SubgraphTemplate>
</Controls:GraphLayout>
This is a simplified example but hopefully it's pretty readable. In excessence I'm trying to bind the VertexTemplate of the SubgraphTemplate to the VertexTemplate of the containing control (the root GraphLayout control). Unfortunately, in the code behind I can see that VertexTemplate is null in the subgraph control.
May be your code can work (this is strange that you bound to something that doesn't have an x:Key and I think you should use {StaticResource SomethingWithName})).
But actually what you looking for is DataTemplateSelector. It's very easy to use and actually support the logic you described.

WPF TreeView: WordWrap

I have this project that displays hierarchical data with huge amounts of text, and I'm transitioning from winforms to wpf, and with winforms treeview not wordwrapping out of the box I really wanna know how to do this in wpf. Is it possible to have Items in the TreeView use word wrapping, out of the box?
I've looped through a fair amount of threads and google results, but none got me any working method. ScrollViewer.HorizontalScrollBarVisibility="false" got me nowhere either.
If its not there, how would one approach the implementation? I'm quite new to wpf, so I'd appreciate a direction to push in.
Oh, and the framework is 3.5.
I think you'd have to bind the width of the root control of the treeview's node template to the actual width of the treeview itself. So something like the following:
<TreeView x:Name="tv">
<TreeView.ItemTemplate>
<DataTemplate TargetType={x:Type TreeViewItem}">
<TextBlock Text="{Binding PropertyToBind}" Width="{Binding ActualWidth, ElementName=tv}" TextWrapping="Wrap"/>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
You should also be able to set the width binding using a relative source, but I can't recall the syntax.
Usually these types of problems are solved by forcibly constraining the element inside of its parent element (even though it should technically already do that). Just an idea. Good luck.

Cross DomainDataSource Combobox SelectedItem Binding

I'm fairly new to Data binding & XAML, so this probably is fairly simple thing but I've been stumped on it for days now (and frustrated with more googling than i can track at this point) and would appreciate any pointers in the right direction. My only preference is to keep it in pure XAML if possible.
In my RIA SL4 project, I have two Entities PackageOS and OS where PackageOS has an association to OS through PackageOS.OS (associating through PackageOS.OSID <-> OS.ID - and [Include] + .Include() setup properly on relevant sections)
This is the template (defined in Page.Resource section along with all other involved DDS) I'm using in DataForm to get OSEntities List to bind into PackageOS Entity (coming from RIA GetOSEntities() using DDS):
<DataTemplate x:Key="POSItemTemplate">
<StackPanel>
<toolkit:DataField Label="PackageOS.OS">
<TextBlock Text="{Binding Source={StaticResource packageOSEntityDomainDataSource}, Path=Data.CurrentItem.OS}" />
</toolkit:DataField>
<toolkit:DataField Label="OS">
<ComboBox ItemsSource="{Binding Path=Data, Source={StaticResource osEntityDomainDataSource}}"
SelectedItem="{Binding Path=Data.CurrentItem.OS, Source={StaticResource packageOSEntityDomainDataSource}}"/>
</toolkit:DataField>
</StackPanel>
</DataTemplate>
The core problem is SelectedItem of ComboBox is not working. All the bindings are reachable from IDE Binding wizard so it's not a problem of me typing incorrect path. I can see packageOSEntityDomainDataSource.Data.CurrentItem to be of type PackageOS.
If i create a manual entry in backend database, the result is shown in PackageOS.OS textblock so I know it is properly being returned but SelectedItem refuses to pick it up (it ends up selecting the first value in dropdown list regardless of OS item in PackageOS).
Many thanks in advance!
Finally figured this out. Leaving my answer in hopes that it saves somebody else the time that I spent on this.
First Lesson
The problem was in the fact that I didn't have a custom Equality implementation for generated entities and default reference equality didn't work as I was using two different instances. Once I implemented IEquatable on my generated entities (through .shared.cs partial classes on server side) everything started working like a charm.
For details please see Silverlight ComboBox Control Population by Manishdalal
Second lesson
Do not use multiple DDS controls if you can help it. Especially once you use a write operation on a DDS, you cannot load/refresh any other DDS that is sharing the DomainContext until changes are committed. The link above shows how to avoid multiple DDS by using list generators when all you want is to pick up list of entities to fill ComboBox up.
My new code looks like this:
<DataTemplate x:Key="POSItemTemplate">
<StackPanel d:DataContext="{Binding Source=packageOSDomainDataSource, Path=Data.CurrentItem}">
<toolkit:DataField Label="OS">
<ComboBox DisplayMemberPath="Name"
ItemsSource="{Binding Path=OSList, Source={StaticResource OSListGenerator}}"
SelectedItem="{Binding Path=OS, Mode=TwoWay}" />
</toolkit:DataField>
</StackPanel>
</DataTemplate>
Where OSListGenerator is returning an IEnumerable<OSEntity> through its OSList property after loading it from DomainContext
Third Lesson
In DDS DataTemplate you have to be explicit with TwoWay Binding. This is the new behaviour; something that took me days to figure as most of the tutorials I referred to were using SL3 and I didn't realize that this was a breaking change in DDS DataTemplate behaviour in SL4.

Handling Mouse events on controls with MVVM pattern - best practice -

I found actually 2 ways to handle mouse events on controls with the mvvm pattern.
Both ways are actually 1 way:
MVVM Light Toolkit by http://mvvmlight.codeplex.com/
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand
Command="{Binding SelectionChangedCommand}"
CommandParameter="{Binding SelectedItems,
ElementName=MyDataGrid}" />
</i:EventTrigger>
</i:Interaction.Triggers>
and the Blend interactivity.dll with Behaviours
<i:Interaction.Triggers>
<i:EventTrigger EventName=”MouseLeftButtonDown”>
<Behaviours:ExecuteCommandAction Command=”{Binding MyCommand}” CommandParameter=”{Binding MyCommandParameter}”/>
</i:EventTrigger>
</i:Interaction.Triggers>
Do you know of any better method?
Moderator: Why the heck are my last 6 xaml lines of code not visible at all?
They are swallowed by IE and Iron browser.
Would you please report the admin to fix that code script? its not working at all very often. prove: http://img251.imageshack.us/img251/5236/errorxt.png
Those are both good ways to do it if you need to handle MouseDown in arbitrary places.
However these situations generally are few and far between. Usually there is a simpler way:
Are you sure your objects aren't really buttons that just don't look like buttons? If so, make them real Button objects and template them to look the way you want.
Are you sure your objects are just selection areas for objects in a list? If so, change the container from ItemsControl to ListBox and restyle ListBoxItem to use the selection areas.
Are your objects graphical paths that are being selected? Use a ToggleButton whose content is the path itself.
There are many other examples of this. In fact, it is uncommon to find a situation in which a MouseDown maps to a Command and there isn't a cleaner way to do the same thing.
There is always another option. You can handle WPF events in the code-behind of the View and call the appropriate method on the ViewModel. The MVVM pattern doesn't forbid to write any code in the code-behind file of the View.
The ViewModel sample application of the WPF Application Framework (WAF) shows how this can work.
XCommand Open source codeplex project has better way to deal with this event based Command/CommandParameter binding. Find here, xcommand.codeplex.com
Here is the sample code below:
<Grid>
<TextBlock Margin="20,30,20,0" VerticalAlignment="Top" Height="80" x:Name="XTextBlock"
Foreground="{Binding FgColor, Mode=TwoWay}"
XCmd:MouseMove.Command="{Binding TextBlockPointerMovedCommand}"
XCmd:MouseLeftButtonDown.Command="{Binding TextBlockPointerPressedCommand}"
XCmd:MouseLeave.Command="{Binding TextBlockPointerExitedCommand}"
Text="{Binding Description, Mode=TwoWay}">
</TextBlock>
<Grid Grid.Column="1" Background="{Binding BgColor, Mode=TwoWay}"
XCmd:MouseMove.Command="{Binding GridPointerMovedCommand}"
XCmd:MouseMove.CommandParameter="{Binding ElementName=XTextBlock, Path=Text}"
XCmd:MouseLeftButtonDown.Command="{Binding GridPointerPressedCommand}"
XCmd:MouseLeftButtonDown.CommandParameter="{Binding ElementName=XTextBlock, Path=Text}"
>
</Grid>
</Grid>
Hope this will be helpful.

WPF Creating a ControlTemplate that is DataBound

I have a control bound to an Object and all is well but I want to turn it into a control template bound to different objects of a similar type. I would like to do this exclusively in xaml, if possible. Any good tutorials that outline the steps?
<TextBlock Text="{Binding Source={StaticResource BorderControl}, Path=ControlName}"/>
EDIT: With a little more experience, it turns out what I need is the ability to Set the Binding source based on a property of the control. i.e.
<TextBlock Text="{Binding Source={StaticResource {TemplateBinding Tag}}, Path=ControlName}"/>
The control exists within a ControlTemplate but works correctly if I bind it directly to the data -- if that makes a difference. I don't know if this is possible or if it's the correct approach. Any thoughts welcome!
EDIT:
This doesn't work either.
<TextBlock Text="{Binding Source={TemplateBinding Tag}, Path=ControlName}"/>
I think you want ContentPresenter here (http://msdn.microsoft.com/en-us/library/system.windows.controls.contentpresenter.aspx) - think of it as one line of an ItemsControl, it's got a content and a reference to a template that will represent that content.

Resources