I have an MVVM Application and want to add a ContextMenu.
I added the ContextMenu to XAML and then set the Items like this (only one item here because it doesn'T matter):
<MenuItem Header="{x:Static Monitor:MonitorResources.R0206_SaveLatestValueToDatabase}"
IsCheckable="true"
IsChecked="{Binding ElementName=root, Path=Model.SaveToDbOneChecked}"
IsEnabled="{Binding ElementName=root, Path=Model.SaveToDbOneEnabled}">
The SaveToDbOneChecked and SaveToDbOneEnabled are Properties in my Model which are implemented like this:
private bool mSaveToDbOneEnabled;
public bool SaveToDbOneChecked
{
get { return mSaveToDbOneChecked; }
set { mSaveToDbOneChecked = value; OnPropertyChanged("SaveToDbOneChecked"); }
}
I set these before the ContextMenu gets called on the SelectionChanged in the GridView the ContextMenu is in. But it won't show the Checked sign next to the text of the MenuItem although the SaveToDbOneChecked has been set to true! I don'T know where i do something wrong and hope that somebody can help me here.
A few things you have to do to make this work. First of all you cannot bind from inside a MenuItem using ElementName property since the target element is most often out of your scope.
If I understand correctly the Model is your ViewModel property, in this case all you have to do is to set it as the DataContext of the Element on which the ContextMenu is placed.
This will set the same DataContext for your MenuItem and you can bind directly to DataContext:
IsChecked="{Binding SaveToDbOneChecked, Mode=TwoWay}"
Related
I have a user control that defines a dependecy property to can use communicate with another user control.
This dependency propperty is in the code behind.
In this user control I have a combobox, from which I want to notify the selected item to the second user control.
The code of the first user control:
public static readonly DependencyProperty TipoComponenteSeleccionadoProperty =
DependencyProperty.Register("TipoComponenteSeleccionado", typeof(TiposComponentes),
typeof(ucClasificacionesComponentesBaseView), new PropertyMetadata(null));
public bool TipoComponenteSeleccionado
{
get
{
return (bool)GetValue(TipoComponenteSeleccionadoProperty);
}
set
{
SetValue(TipoComponenteSeleccionadoProperty, value);
}
}
I was trying something like that in the xaml:
<ComboBox Name="cmbTiposComponentes" Width="150"
TipoComponenteSeleccionado="{Binding ElementName=cmbTiposComponentes, Path=SelectedItem}">
The idea is when I select the item in the combobox, update the dependency property, so the second user control can bind it and be notified.
But I get an errror because the dependency property can't be used in the combobox.
So I was wondering if there is some way to use the dependency property in the combobox.
I have tried to define a static resource in the xaml of the first user control, something like that:
<UserControl.Resources>
<local:MyMainUserControl.DependencyProperty Key.../>
</UserControl.Resources>
But the itellisense shows me all the views that I have except this, so I don't have access to the depedency property of the view.
So is it possible to use the dependency property in the view in which it is defined?
Thanks.
bind SelectedItem to TipoComponenteSeleccionado:
<ComboBox Name="cmbTiposComponentes" Width="150"
SelectedItem="{Binding Path=TipoComponenteSeleccionado, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type local:TiposComponentes}}}">
I have a WPF DataGrid in a Window with associated View(*.xaml.cs) and ViewModel that is successfully executing a bunch of functionality. However, functions that modify items instead of altering the collection do not update until a sort, resize, etc.
I've found a bunch of search results suggesting the solution is to make the item type implement INotifyPropertyChanged and add/subtract event handlers as appropriate for every item in the collection. I tried for a bit using those examples without success, but frankly it doesn't seem like a great option.
The item type is declared elsewhere in the app and is sharing the object instances with other modules, so I would like not to modify that class. It also seems like a poor design to tie the implementation of the DataGrid's ItemsSource to the item type within; the list container is already an ObservableCollection invoking OnPropertyChanged as needed already, so why should that not be sufficient?
I'm able to update via DataGrid.Items.Refresh() - which unfortunately does not seem to have an overload for specific items/properties instead of updating the entire list, but that's a minor issue - but only my View has a reference to the DataGrid itself (per MVVM), whereas the Command binding is in the ViewModel.
I would actually like to put those Command bindings in the View, and I don't understand why convention is to put those in the VM and thereby bypass the View during a UI event. For example, to delete items I can select them and either press Delete (KeyUp handler is in the View, which then passes selected items as a list to the VM) or select Delete in the context menu (Binding is to an ICommand in the VM, which routes to the same function invoked by the View). Why would it not be more desirable to bind both to the View's event handler (or two handlers both in the View)?
I've seen some results that use a RelativeSource for the Command binding to an ancestor of type UserControl...I've tried with type Window to try to bind to the View's method for naught. As of now my best option is to put a delegate event RefreshListItems on the VM and subscribe to it from the view with a function that invokes DataGrid.Items.Refresh().
This is workable, but in the interest of edification I wondered if anyone could tell me how to bind Command properties to the View (a Window) instead of the ViewModel, and/or how to notify the control bound to my ObservableCollection to refresh either specific or all items from the ViewModel without implementing the INotifyPropertyChanged scheme on every list item?
Edit per mm8's suggestion:
I tried your code for the ICommand in my view, but I get this error:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1''. BindingExpression:Path=SetService; DataItem=null; target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand')
That's with each of these attempts, with and without CommandParameter, bound to an ICommand of type either RelayCommand or DelegateCommand:
<MenuItem Header="Set Service" Command="{Binding SetService, RelativeSource={RelativeSource AncestorType=Window}}"/>
<MenuItem Header="Set Service" Command="{Binding SetService, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
<MenuItem Header="Set Service" Command="{Binding SetService, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"/>
.xaml:
<Window>
…
<DataGrid x:Name="TheGrid" ItemsSource="{Binding Source={StaticResource MessageItems}}" KeyUp="MessageList_KeyUp" AutoGenerateColumns="False" IsReadOnly="True" ColumnWidth="Auto">
…
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Set Service" Command="{Binding SetService, RelativeSource={RelativeSource AncestorType=Window}}"/>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
…
</Window>
.xaml.cs:
public partial class TheGridView : Window
{
TheGridViewModel _viewModel;
public ICommand SetService;
[ImportingConstructor]
public TheGridView(TheGridViewModel vm)
{
DataContext = _viewModel = vm;
vm.RefreshListItems += () => TheGrid.Items.Refresh();
InitializeComponent();
Closing += Window_Closing;
SetService = new RelayCommand(SetSvc);
}
private void SetSvc(object selectedItem)
{
// Doesn't get here
}
}
This is workable, but in the interest of edification I wondered if anyone could tell me how to bind Command properties to the View (a Window) instead of the ViewModel?
There is nothing that stops you from defining ICommand properties in the code-behind of the view and bind to them like this (assuming your view is a Window):
Command="{Binding YourCommandProperty, RelativeSource={RelativeSource AncestorType=Window}}"
Edit:
A ContextMenu resides in its own visual tree but you should be able to bind to the parent window through the Tag property of the DataGrid, something like this:
<DataGrid x:Name="TheGrid" ItemsSource="{Binding Source={StaticResource MessageItems}}" KeyUp="MessageList_KeyUp" AutoGenerateColumns="False" IsReadOnly="True" ColumnWidth="Auto">
<DataGrid.Tag>
<Binding Path="." RelativeSource="{RelativeSource AncestorType=Window}" />
</DataGrid.Tag>
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Set Service" Command="{Binding PlacementTarget.Tag.SetService, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
...and/or how to notify the control bound to my ObservableCollection to refresh either specific or all items from the ViewModel without implementing the INotifyPropertyChanged scheme on every list item?
You can't do this unless you refresh the entire control (for example using DataGrid.Items.Refresh()). That's why you should implement INotifyPropertyChanged.
If you currently bind to some class that is shared across several modules and you don't want to modify this class, you could create a new client-specifc wrapper class that does implement INotifyPropertyChanged and bind to this one instead of binding to the common class, e.g.:
public class Wrapper : INotifyPropertyChanged
{
private readonly SharedModel _model;
public Wrapper(SharedModel model)
{
_model = model;
}
private string _property;
public string MyProperty
{
get { return _property; }
set { _property = value; OnPropertyChanged(); }
}
//...
public event PropertyChangedEventHandler PropertyChanged;
}
I have a usercontrol which I am including in my datagrid column.
But I want to hide that usercontrol on the basis of certain condition.
I tried binding the visibility of that usercontrol but that is not working.I dont know why is that happening.
<DataGrid>
... .
...
<UserControl:MyUserControl Visibility="{Binding
SomeProperty,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
</DataGrid>
But if I am using the same property in hiding the other controls present in the DataGrid columns.They are working and getting hide on the certain condition.
I dont know what is happening.Please help me guys.
Try to explicitly set the DataContext property of your UserControl:
<UserControl:MyUserControl DataContext="{Binding}" Visibility="{Binding
SomeProperty,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
Otherwise show the full xaml for dataGrid.
I am quite sure that there is a binding error coming in Output or Immediate Window.
Why?
Because your control is looking for SomeProperty in the collection entity which is not present there.
That property as you suggested is available at the Page ViewModel level.
Therefore do this
<UserControl:MyUserControl
Visibility="{Binding DataContext.IsVisible,
RelativeSource={RelativeSource FindAncestor,
AncestorType=YourUserControl}" />
Since your control is nested in a DataGrid, the DataContext of your databinding is the same as your DataGrid context.
Therefore, you can either choose to bind the visibility of your element to a property of the DataGrid DataContext or bind it to a property of a relative UI parent DataContext or a prox.
I'll take the following model as example.
public class ViewModel
{
public class Foo
{
public int Id {get;set;}
public Visibility IsVisible {get;set;}
}
private IList<Foo> _fooList;
public IList<Foo> FooList {get;set;}
private Visibility _parentVisibility;
public Visibility ParentVisibility{get;set}
}
Solution 1 (DataGrid datacontext) :
Assuming your datagrid is bound to FooList you can straight forward bind to the IsVisible property of you Foo object as :
<UserControl:MyUserControl Visibility="{Binding IsVisible}" />
Solution 2 (Parent context) :
If you want to bind the visibility of your UserControl to a property that is not in the datagrid context you either have to specify a RelativeSource to your binding or a DataContextProxy.
Going for the RelativeSource solution you can bind the visibility of your UserControl to the ParentVisibility property as :
<UserControl:MyUserControl Visibility="{Binding DataContext.ParentVisibility, RelativeSource={RelativeSource FindAncestor, AncestorType=YourPageUserControl}" />
Hope this helps.
I have two user controls LeftPanel and DeviceList. DeviceList is inside the LeftPanel. I want the context menu in device list to be able to call a command on the parent view model, which is set on a grid that hosts DeviceList. I have tried the following but it does not work.
Here is the ContextMenu within DeviceList
<MenuItem Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type local:LeftPanel}},
Path=DeviceListViewModel.RemoveDevice}">
Here is the LeftPanel usercontrol
<UserControl x:Class="Tool.LeftPanel" .... >
<Grid DataContext="{Binding DeviceListViewModel}" Grid.Column="1">
<local:DeviceList Grid.Row="1" Margin="0,0,0,10"/>
I had a similar problem, but the ContextMenu does not see the DataContext of the ViewModel or the Parent.
A possible solution is here Access ViewModel / DataConext inside ContextMenu .
the contextmenu is not part of the visualtree, so i think your binding is simply wrong because the datacontext is not what you think of.
use Snoop to check your DataContext and bindings at runtime
i assume that you have to work with PlacementTarget in your bindings
So I've got a listview and it's itemsSource property bound to an ObservableCollection, placed on the view model. And a button the on view
How do I make it so the button deletes the item, selected on the listview, from the observableCollection?
Just to add another way to do it:
<ListView ItemsSource="{Binding MyList}"
SelectedItem="{Binding SelectedItem}"/>
<Button Command="{Binding DeleteCommand}"/>
In your ViewModel you have a property called SelectedItem that will be updated every time you change the selection in the ListView.
Now you can handle deletion in the ViewModel easily:
internal class ViewModel
{
public ViewModel()
{
this.DeleteCommand = new RelayCommand(() => this.Delete());
}
public void Delete()
{
this.MyList.Remove(this.SelectedItem);
}
}
Go HERE for further information about RelayCommand
There are many ways to do this. One way is to create a RelayCommand or a DelegateCommand with Parameter
<Button Command="{Binding MyDeleteCommand}"
CommandParameter="{Binding ElementName=mylistview, Path=SelectedItem}"/>
You can go the way AlSki posted and bind the SelectedItem to your ViewModel and handle the command without parameter in your ViewModel
Bind a second property to the list views selected item, and a third to a Command on the view model, that simply removes the selected from the list of items.
See http://msdn.microsoft.com/en-us/magazine/dd419663.aspx