Heres a simple question. I've got a XAML that at the moment looks like this:
<ListBox>
<ListBox.ContextMenu>
<ContextMenu DataContext="{Binding Path=FeedContextMenu}"
</ListBox.ContextMenu>
</ListBox>
My intention is to take the data context from the ListBox and use its FeedContextMenu property as a DataContext for ContextMenu. Now all you WPF gurus will probably immediately say that this will not work. Apparently, this has something to do with the fact that ContextMenu isn't part of the visual tree. Now I'm not a WPF expert, so after googling for hours and trying out different suggested solutions that didn't work nor make any sense to me whatsoever, I would like to ask someone with a greater knowledge to explain it to me what and why needs to be done in order for this to work. Thanks.
You could try to bind to the DataContext of the ContextMenu's PlacementTarget (which is the ListBox) like this:
<ContextMenu DataContext="{Binding PlacementTarget.DataContext.FeedContextMenu,
RelativeSource={RelativeSource Self}}">
Related
I have a ViewModel which that is defined in my application resources, this ViewModel has a command called RunCommand
and in my MainWindow i am trying to bind that command to a button without setting the datacontext so i tried
<Button Command="{Binding Source={StaticResource ViewModel.RunCommand}}"/>
it showed an exception, however when i do the following things work fine
<Button DataContext="{Binding Source={StaticResource ViewModel}}" Command="{Binding RunCommand}"/>
what is wrong with the first part, and do i have to set the datacontext for such a simple task?
You are certainly not forced to change/set the DataContext just so you can bind a simple property.
Here's what you want
<Button Command="{Binding RunCommand, Source={StaticResource ViewModel}}"/>
Setting the datacontext is a good thing to do ... it takes away the voodoo of what object you're talking to. I believe all MVVM frameworks help you out with Locators, and when not using them, you can use your code behind.
It's just the way the language works.
We have a WPF application with a standard MVVM pattern, leveraging Cinch (and therefore MefedMVVM) for View -> ViewModel resolution. This works well, and I can bind the relevant controls to properties on the ViewModel.
Within a particular View, we have an Infragistics XamGrid. This grid is bound to an ObservableCollection on the ViewModel, and displays the appropriate rows. However, I then have a specific column on this grid which I am trying to bind a TextBox text value to a property on the parent DataContext, rather than the ObservableCollection. This binding is failing.
We've gone through several options here including:
Using AncestorType to track up the tree and bind to the DataContext of the parent UserControl like so (from the great answer to this question, as well as this one)...
{Binding Path=PathToProperty, RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}
Specifying the ElementName and trying to target the top level control directly. Have a look here if you'd like to read about using ElementName.
Using a 'proxy' FrameorkElement defined in the resources for the UserControl to try and 'pass in' the context as required. We define the element as below, then reference as a static resource...
<FrameworkElement x:Key="ProxyContext" DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource Self}}"></FrameworkElement>
In this case the binding finds the FrameworkElement, but can not access anything beyond that (when specifying a Path).
Having read around, it looks quite likely that this is caused by the Infragistics XamGrid building columns outside of the tree. However, even if this is the case, at least options 2 or 3 should work.
Our last thoughts are that it is related to the V - VM binding, but even using Snoop we've yet to find what the exact issue is. I'm by no means an expert with WPF binding so any pointers would be appreciated.
EDIT: I have found some templating examples from Infragistics here that I will try.
EDIT 2: As pointed out by #Dtex, templates are the way to go. Here is the relevant snippet for use with a XamGrid:
<ig:GroupColumn Key="CurrentDate">
<ig:GroupColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DataContext.CurrentDateTest, RelativeSource={RelativeSource AncestorType=UserControl}}" />
</DataTemplate>
</ig:GroupColumn.HeaderTemplate>
<ig:GroupColumn.Columns>
I've left the XML open... you'd simply add the columns you wanted, then close off the relevant tags.
I dont know about XamGrid but that's what i'll do with a standard wpf DataGrid:
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding DataContext.MyProperty, RelativeSource={RelativeSource AncestorType=MyUserControl}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding DataContext.MyProperty, RelativeSource={RelativeSource AncestorType=MyUserControl}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Since the TextBlock and the TextBox specified in the cell templates will be part of the visual tree, you can walk up and find whatever control you need.
Because of things like this, as a general rule of thumb, I try to avoid as much XAML "trickery" as possible and keep the XAML as dumb and simple as possible and do the rest in the ViewModel (or attached properties or IValueConverters etc. if really necessary).
If possible I would give the ViewModel of the current DataContext a reference (i.e. property) to the relevant parent ViewModel
public class ThisViewModel : ViewModelBase
{
TypeOfAncestorViewModel Parent { get; set; }
}
and bind against that directly instead.
<TextBox Text="{Binding Parent}" />
i've been banging my head on this for the last hours...
I have a User Control called "DayItem", and i want to show it 48 times in another UserControl called "DayPanel".
Let me mention this is done in MVVM style, but i'm only experiencing, and a straight way would by fine for an answer.
I have an ObservableCollection<DayItem> in the DayPanel model, and in the Xaml there's an <ItemsPresenter />.
if i do
this.ItemsSource = DayItems;
everything show up fine.
but, i wanna be able to use those DayItems in the UI like a list... to support multi-select etc.
so i tried using a ContentControl, and set it's content to the ObservableCollection.
but it just shows the ObservableCollection object's ToString text.
so i guess i need a DataTemplete there...
but why do i need a DataTemple to show a Control?
it's already styled in it's own Xaml, i don't wanna repeat it's styling again.
or maybe i'm totally wrong, anyway i need help :x
Edit:
I got this to work, saying what DataType wasn't necessary or even possible.
and in the code behind i told the listbox, that it's ItemSource was the ObservableCollection.
now i've ran into other problems... ListBox related...
There are Gaps between each control in the ListBox, which messes up the layout
and also i need to figure out a way to select multiple items by dragging...
thanks for the help so fat
First, you need a view model for you DayItem user control. Lets call it DayItemViewModel. Also I suppose you DayPanel also has a view model called something like DayPanelViewModel. Then, you DayPanelViewModel would expose a collection of DayItemViewModel instances:
public class DayPanelViewModel
{
public ObservableCollection<DayItemViewModel> DayItems { get; set; }
}
Then, in your DayPanel.xaml:
<UserControl x:Class="DayPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Resources>
<DataTemplate x:Key="DayItemTemplate"
DataType="{x:Type my:DayItemViewModel}">
<my:DayItem />
</DataTemplate>
</UserControl.Resources>
<Grid>
<ListBox ItemsSource="{Binding DayItems}"
ItemTemplate="{StaticResource DayItemTemplate}" />
</Grid>
</UserControl>
Try using ListBox, since that implements multiselect...
Also it might be wise (for MVVM) if you do not contain DayItems, but DayItemModel's in your DayPanelModel, and set the ListBox's ItemTemplate to present each DayItemModel with a DayItem.
In a question I had posted before regarding databinding and UserControls, I was having problems getting simple properties set so that I could change colors, size, etc. Kent gave me some great pointers and that worked great. I then authored a new UserControl, and using his advice, had that working great as well.
Now I'm at the next step -- databinding commands. My current structure looks like this:
Window --contains--> UserControlB --contains--> UserControlA
Now databinding properties in UserControlA work great, and my UserControlB exposes these same properties so that the Window can change UserControlA indirectly. The problem is that UserControlB's DataContext is set something like this:
<UserControl x:Name="root">
<Grid DataContext="{Binding ElementName=root}">
...
<Button Command="{Binding MyCommand}" />
...
</Grid>
</UserControl>
But I want MyCommand to be bound to my ViewModel. I thought it wass possible to set the DataContexts separately, but how do I get the Buttons to point to my ViewModel in XAML?
I found a related post, but didn't sound like what I want to do. I want to create the ViewModel in code, not in XAML.
Your binding should look something like this:
<Button Command="{Binding Path=DataContext.MyCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TypeOfYourControlWithViewModelDataContext}}"/>
I have some code that looks like this:
<Expander Header="{Binding SelectedSlot.Name}"
Visibility="{Binding ShowGroupSlot, Converter={StaticResource BooleanToVisibility}}">
<Controls:GroupPrototypeSlotControl Slot="{Binding DataContext.SelectedSlot,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Expander}}}" />
</Expander>
This works, but the ugliness of the Slot Binding bothers me. This is required because the GroupPrototypeSlotControl has a GroupPrototypeViewModel as its DataContext. If I simply use {Binding SelectedSlot}, it attempts to resolve it on the 'child' ViewModel, which fails. I get around this by explicitly looking at the DataContext of my parent control. Is there a cleaner way to do this type of binding?
EDIT: I found a cleaner way of resolving my problem, though it still feels like a hack. I modified the GroupPrototypeSlotControl so that it has a top-level LayoutRoot (a StackPanel, in this case) and then set the DataContext of LayoutRoot to the ViewModel rather than setting the DataContext of the entire control. This allows me to use the {Binding SelectedSlot} syntax where I use the control (since the control still has the parent DataContext), at the cost of slightly increasing the complexity of the control. In general, this is probably the better pattern for a custom control, since the consumer of the control expects a {Binding} to resolve to their parent DataContext if one isn't explicitly specified.
A slightly cleaner (shorter) way is to use ElementName in your Binding like this:
<Expander Header="{Binding SelectedSlot.Name}"
x:Name="expander"
Visibility="{Binding ShowGroupSlot, Converter={StaticResource BooleanToVisibility}}">
<Controls:GroupPrototypeSlotControl Slot="{Binding DataContext.SelectedSlot, ElementName=expander}" />
</Expander>