Databinding to functions in dynamically-loaded plugins - wpf

I have several MenuItems whose Commands are bound to my ViewModel. Until today, all of them execute properly.
Now I have added a MenuItem whose ItemsSource is bound to an ObservableCollection. The point of this MenuItem is to enumerate a list of plugins so that the name of all plugins show up. Then when the user clicks on a plugin name, it should call a function to display properties for audio filters.
In my current implementation, which doesn't work, I tried to databind like this:
<MenuItem Header="Filters" ItemsSource="{Binding FilterPluginNames}">
<MenuItem.ItemContainerStyle>
<Style>
<Setter Property="MenuItem.Command" Value="{Binding ShowFilterDialogCommand}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
The problem is that I get a BindingExpression path error because it's trying to use a String as the MenuItem's DataContext.
This leads me to believe that the DataContext for a MenuItem's MenuItems is automatically set to type of objects in the ItemsSource. Is this true?
If I need to change the DataContext, then I'd like to change it to the ViewModel that handles all of my other Commands. But if I do that, how in the world am I able to tell which plugin I want to display filter properties for? I'd need to pass in a CommandParameter at the very least, but binding this value to the filter name isn't my most favorite option. Are there any other ways to do this?
If the DataContext is indeed automatically set to the object type in the ObservableCollection, then I'd rather just call my interface method ShowFilterProperties() directly. I bet that I can't do this without Command binding. If that is the case, how do you all deal with this sort of application? Do you make all of the plugins expose a command handler, which will then show the dialog?
EDIT -- I modified my code to change the ObservableCollection type, and sure enough, WPF wants to databind to the type T. So I guess one option is to have the plugin expose the ICommand, but I don't know if this is a weird approach or not?
EDIT -- ok, I just learned something new. Interfaces can't have fields, so is it not possible to databind with plugins, period?

You are likely not quite binding like you think you are. You might want to just put some diagnostics on your bindings and see what object they are binding to. Here is a good link for debugging bindings:
http://www.beacosta.com/blog/?p=52
Here's a sample:
<Window …
xmlns:diagnostics="clr-namespace:System.Diagnostics;assembly=WindowsBase"
/>
<TextBlock Text="{Binding Path=Caption, diagnostics:PresentationTraceSources.TraceLevel=High}" … />
I think your approach is correct... it probably just needs to be debugged a little.

Related

SelectedItem of SelectedItem

first of all I would like to thank you for the many good posts that i read in this forum. Unluckily I could not find anything of help for my current problem (either here or anywhere else).
What I'm trying to do sounds quite simple, but I have no clue how to get it to work ... perhaps I'm still to new to wpf or I don't thing wpfy enough :)
I'm designing a front end for a part in an automated manufacturing:
I have a quantity of places where pallets can be put (but it can be empty as well).
Each pallet has up to 3 places where parts can be mounted
Everything is created dynamically of a database and is reacting to changes.
The position of the parts on the pallet comes from the database as well and should be visualized
What I would like to have is:
An overview over the pallet-places with a preview of the pallet
When I select a place I want to see a detail view of the place
When I click on a part on the pallet of the detailed pallet I want to see details to the part
The first two points are quite simple and work nicely:
I have got a DataTemplate for every component (part, pallet, pallet-place). Actually those are UserControls that are imported as Datatemplates
the overview is a ListBox with the places as DataContext
for the detail-place-view I use the UserControl and bound it to the SelectedItem of the Listbox
I tried to bind the Text of a Textblock to the ID of the selected Part ... and fail.
Probably I could use some global variables in the code behind - but that sound very ugly.
Can anybody help?
I have got a solution ... it is not nice but works.
I created an event in the pallet, that triggers, when the selected part-place changes
I handle the event in the pallet-place and create a new one
And finally I handle it in the overview and change the detailview accordingly
Most likely there are much nicer solutions, but it will suffice.
Perhaps try an ElementName binding?
<TextBlock Text="{Binding ElementName=Name_of_your_Listbox, Path=SelectedItem.ID" />
Can you post a bit more code of your TextBlock and your Binding?
Context is important, if i use a ContentControl and bind its content to the SelectedItem like this:
<ContentControl Content="{Binding SelectedItem, ElementName=mylistbox}">
I can bind to the ID of the selected item in the DataTemplate like this:
<ContentControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding ID}" />
</DataTemplate>
</ContentControl.ContentTemplate>
That is because setting the Content of the ContentControl automatically sets the DataContext as well, and this binding is relative to the DataContext since no source (ElementName, RelativeSource, Source) has been specified.
I do not know how your UserControl handles the context, if the DataContext is not affected such bindings will not work. You would need to bind directly then:
<uc:MyDetailsView Data="{Binding SelectedItem, ElementName=mylistbox}">
<!-- ... -->
<TextBlock Text="{Binding SelectedItem.ID, ElementName=mylistbox}" />
This of course defeats the purpose of having the binding on the UserControl itself in the first place. But unless you post some relevant code it's quite hard to tell what is wrong.
Also check the Output window in VisualStudio, binding errors will show up there and might provide valuable information as to what went wrong.

Get reference to Xaml object in view model

I have a an object created in Xaml:
<Grid>
<MyObject/>
</Grid>
I need someway to bind the object myObject back to a property in my view model. I dont know whether this is possible, everything ive seen so far binds properties together, but any help would be greatly appreciated.
I am assuming what you want is your ViewModel to hold the actual visual control MyObject in it and your Grid to display it via MVVM.
This is possible through ContentControl in WPF.
Assuming your ViewModel has a property MyObjectView which holds MyObject...
<Grid>
<ContentControl Content="{Binding MyObjectView}" />
</Grid>
Having said that you must take caution that same MyObjectView is not bound to any other content control as that will result in an error
"Specified element is already the logical child of another element.
Disconnect it first"
And if that requirement is possible then you must excercise ContentTemplate option.
Let me know if this helps.
It is possible. It kinda breaks mvvm though.
You can attach an InvokeCommandAction to this object, and bind the CommandParameter to it via ElementBinding. Then in the callback of the command which you defined in the viewmodel, you will have a reference to this object from the CommandParameter.

Binding WPF ContextMenu MenuItem to UserControl Property vs ViewModel Property

I'm struggling to understand what is going on with the ContextMenu. I know it is rendered as a separate window, with a separate visual tree, so we can't use relative binding to bind a command exposed as a property of the user control. e.g. the following does not work:
<MenuItem Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=TestCommand}" Header="Test" />
But, if you set the data context of the user control to a view model that exposes a command as a property, the following will work:
<MenuItem Command="{Binding TestCommand}" Header="Test" />
What I don't understand is, how is the ContextMenu inheriting the value of the DataContext if it is not part of the visual tree. I would expect both of these examples to behave the same (i.e. both work or both fail).
The second binding works because of so called "inheritance context". You can read about it here: http://blogs.msdn.com/b/nickkramer/archive/2006/08/18/705116.aspx. Basically this is a kind of special case where some properties inherit data context of the owner object. Thus, for example, the inheritance context always works on properties of type Freezable (another interesting article about Freezables: http://drwpf.com/blog/category/freezables/).
Actually the article says that the inheritance context doesn't work on ContextMenu, but in version 4 they added it, so it actually works now as you've shown it in your example.

How to set ItemsSource?

This dialog makes no sense to me
http://img576.imageshack.us/img576/4223/50709706.gif
And I'm having trouble finding good tutorials on it. Most of the examples aren't detailed enough, or do stuff via code, but I'd like to take advantage of the IDE as much as possible.
Whats the difference between ItemsSource and DataContext?
I'd like to bind it to just a List for starters. I don't need SQL or databases or anything fancy. Where would I declare my list? In MainWindow.xaml.cs? How do I get it to appear in that dialog?
Think of "DataContext" as the default value for "Source" in a binding.
When you create a binding, you can specify the path and source, like this (I'll use TextBox as an example):
<TextBox Text="{Binding Path=Foo,Source={StaticResource Bar}}" />
So my TextBox.Text property is bound to a Foo property on an object called Bar (a resource somewhere in the application).
However, if you have a whole bunch of things that you want to bind to properties on Bar, it's easier to set Bar as the DataContext of the parent container. A Binding without a Source will just use the DataContext by default, and DataContext flows through to child controls from the parent. So:
<StackPanel DataContext="{StaticResource Bar}">
<TextBox Text="{Binding Path=Foo}" />
<TextBox Text="{Binding Path=Fizz}" />
<TextBox Text="{Binding Path=Buzz}" />
</StackPanel>
All of the TextBoxes are still binding to properties on Bar, but they're doing it without setting it as a Source explicitly.
So let's have another look at the dialog you posted. It's giving you several options for the "source" of the ItemsSource binding. When you choose "DataContext", you're telling Visual Studio that the ItemsControl doesn't need to know the source - it'll pick it up from the DataContext of the parent container (maybe even the Window itself).
If you chose one of the other options (ElementName, RelativeSource or StaticResource) then you'd be setting the binding's source explicitly for that ItemsControl.
Once you've told it that it's binding to the DataContext, you'll need to drop into the "Path" section of the dialog and tell it which property to bind the items of the control to. In the end, the markup would look something like this (assuming it's a ListBox):
<ListBox ItemsSource="{Binding Path=Foos}" />
So the items in the ListBox are coming from a property called "Foos", and that property is on an object that's set in the DataContext somewhere higher in the logical tree (perhaps on the Window itself).
You rarely need to use the data context of a control outside of the control. The most common use case for setting DataContext(DataContext = this;) is within UserControl's code-behind to make all controls within the UserControl to bind to the control's properties.
When you use a ListBox, setting ItemsSource is sufficient, unless you are doing something funky.
This is a pretty good walkthrough: http://windowsclient.net/learn/video.aspx?v=315275
Specifically, you need to set the DataContext first to tell it where to look for the ItemsSource. The easiest way is to set this on the Window through the XAML:
<Window.DataContext>
<controllers:DownloadManager />
</Window.DataContext>

WPF contextmenu and ListView

Ok, hopefully this is simple but for some reason I can't find a straight answer and I'm not familiar enough with WPF yet to know how to do it.
I have a listview, it gets bound to an observable collection of objects to display. I want to have a context menu with a bunch of options. The options in the context menu are relative to the particular object in the list that was clicked on (things like delete, export, etc).
So I need the object that the user right clicked on in my listview to be passed as a parameter to the command that the context menu executes.
How do I do this?
Edit: I should mention I would prefer a solution that is mostly (if not entirely) xaml - I'm trying to avoid having significant code in the code-behind. If that's the only way to do it though...
Further Edit: More details that I forgot to mention that are important. The command I want executed is on the object bound to the data context of my user control, it is not on the objects in the list view. So I need the context menu's on the list view's items to be bound to a command that is on the user control's data context, and the listview item passed as a parameter into that command.
It depends on whether your ContextMenu is part of the template for individual items, or if it is attached to the ListBox as a whole.
If you are attaching your ContextMenu to the items in the list using a DataTemplate (this is generally the best way to do it), the DataContext on the MenuItem is already set so all you need to do is:
<MenuItem ... CommandParameter="{Binding}" />
On the other hand, if your ContextMenu is attached to the ListBox as a whole, you'll need to access the SelectedItem property of the ListBox:
<MenuItem ... CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource FindAncestor,ListBox,1}} />

Resources