I am using WPF and I have a custom datagrid. What I would like to do is add a property (CustomGridCommands) to that grid which I can set in xaml from any view.
What I have at the moment is as follows (I have changed the code a bit to simplify it):
Custom grid C# code:
public class CustomWPFDataGrid : DataGrid, INotifyPropertyChanged
{
public static readonly DependencyProperty CustomContextMenuCommandsProperty =
DependencyProperty.Register("CustomContextMenuCommands",
typeof (ObservableCollection<WPFBaseCommand>),
typeof (CustomWPFDataGrid));
[Bindable(true)]
public ObservableCollection<WPFBaseCommand> CustomContextMenuCommands
{
get { return (ObservableCollection<WPFBaseCommand>) GetValue(CustomContextMenuCommandsProperty); }
set { SetValue(CustomContextMenuCommandsProperty, value); }
}
...
...
}
XAML code:
<common:CustomWPFDataGrid
ItemsSource="{Binding Path=ItemList}"
CustomContextMenuCommands="{Binding Path=CustomGridCommands, Mode=TwoWay}">
....
</common:CustomWPFDataGrid >
The object I have bound to the view that contains the grid is as follows:
public class TestViewModel
{
public ObservableCollection<TestDisplayViewModel> ItemList { get; set; }
public ObservableCollection<WPFBaseCommand> CustomGridCommands;
public TestViewModel()
{
... population of objects here
}
When I run this, and check the value of the property (CustomContextMenuCommands) in the datagrid, it is always null.
Any ideas what I am doing wrong?
EDIT
The setter of the "CustomContextMenuCommands" is never hit.
CustomGridCommands in your ViewModel is a field, View cannot use it. If you make it a public property, then it will become accessible. More details on what can be used as binding source can be found on MSDN - Binding Sources.
If using WPF 4.5, static properties can also be used for binding, as described in release notes.
Related
I have a class "Application" which has a static member(MainWindow, inherits from DXWindow) like the following:
public static MainWindow MainWindowInstance
I instantiate and display it:
MainWindowInstance = new MainWindow();
MainWindowInstance.ShowDialog();
MainWindow has a property:
private Thickness _localAttachmentsButtonsMargin = new Thickness(0, 0, 5, 0);
public Thickness LocalAttachmentsButtonsMargin {
get {
return _localAttachmentsButtonsMargin;
}
set {
_localAttachmentsButtonsMargin = value;
NotifyPropertyChanged("LocalAttachmentsButtonsMargin");
}
}
I need to access the LocalAttachmentsButtonsMargin property from many other controls (like UserControls) in xaml.
What I tried so far is:
--> Access it with x:Static:
Margin="{Binding Path=MainWindowInstance.LocalAttachmentsButtonsMargin, Source={x:Static Application.Current}}"
--> Giving the UserControl a x:Name ("UcTmp"), provide a getter in the UserControl and access the property from code behind
Margin="{Binding ElementName=UcTmp, Path=LocalAttachmentsButtonsMargin}}"
public Thickness LocalAttachmentsButtonsMargin {
get {
Application.MainWindowInstance.LocalAttachmentsButtonsMargin;
}
}
But none of them both seems to work. I think I need the xaml equivalent to the one line of code in the getter given above.
An additional requirement is that the property in MainWindow needs to be updatable. Ideally the solution is able to update the view which is represented throught the UserControl if the property will be updated though some code.
Since you can only bind to properties, you should define MainWindowInstance as such:
public static MainWindow MainWindowInstance { get; set; }
It cannot be a field:
public static MainWindow MainWindowInstance;
I am doing a simple WPF application using MVVM and I am having trouble binding to the SelectedItem property of the combobox.
The setter of the bound property does not get called, and there is no output in the debug windows telling me it is not able to bind (I assume it is able to).
This is .NET 3.5, I made a small example that has the same problem.
In XAML:
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel>
<ComboBox IsDropDownOpen="False" IsReadOnly="False" ItemsSource="{Binding Path=Printers}" SelectedIndex="0" SelectedItem="{Binding Path=Printer.SelectedPrinter, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Name="cmbPrinters" />
</StackPanel>
</Window>
View code behind:
using System.Windows;
public partial class Window1 : Window
{
ViewModel viewmodel;
public Window1()
{
viewmodel = new ViewModel();
this.DataContext = viewmodel;
InitializeComponent();
}
}
View model:
using System;
using System.Collections.ObjectModel;
public class ViewModel
{
public ViewModel()
{
Printers = new ObservableCollection<string>() { "test", "test2" };
Printer = new PrinterViewModel();
}
public PrinterViewModel Printer { get; set; }
public ObservableCollection<string> Printers { get; set; }
}
PrinterViewModel:
using System.Windows;
using System.Diagnostics;
public class PrinterViewModel : DependencyObject
{
public string SelectedPrinter
{
get { return (string)GetValue(SelectedPrinterProperty); }
set
{
SetValue(SelectedPrinterProperty, value);
Debug.WriteLine("!!!!!! SelectedPrinter setter called");
}
}
public readonly DependencyProperty SelectedPrinterProperty =
DependencyProperty.Register("SelectedPrinter", typeof(string), typeof(PrinterViewModel), new UIPropertyMetadata());
}
Can anyone see what I am doing wrong here?
The problem here is that you have a misunderstanding about how the Silverlight dependency property system works.
When the value of a dependency property changes, Silverlight doesn't go through the properties you've defined (such as SelectedPrinter) to set the value of the dependency property. The Silverlight dependency property mechanism keeps track of all of the values of dependency properties, and when the value of one of these properties changes, Silverlight changes its value directly without calling your code to do so. In particular, it will not call your property setter. This should explain why your debugging message wasn't appearing.
The getter in a property that uses a dependency property, such as your SelectedPrinter property, should contain only a call to GetValue, and the setter should contain only a call to SetValue. You shouldn't add any code to the getter or setter, as doing this will not achieve what you want.
Furthermore, you are using dependency properties in the view-model layer. This is not where they are intended to be used. Dependency properties are only intended to be used in the view-layer. Your view-model classes should instead be implementing INotifyPropertyChanged rather than extending DependencyObject.
It is possible to bind two dependency properties together. This is permitted, and occasionally it comes in useful to wire together two dependency properties in the view-layer. In fact, the bindings in your example were working, which explains why you weren't getting any messages about problems with bindings.
why inherit from DependencyObject when doing mvvm?`
public class PrinterViewModel : INotifyPropertyChanged
{
private string _selected;
public string SelectedPrinter
{
get { return this._selected; }
set
{
_selected= value;
OnPropertyChanged("SelectedPrinter");
}
}
}
now your code should work
I am looking for a way to create an UserControl in silverlight 4 and expose a dependency property, which can accept any type. What I mean by that is, for example, if you look at standard silverlight control like AutoCompleteBox, it is capable of handling any type of collections. So you can bind AutoCompleteBox with IEnumerable<Human> or IENumerable<Animal> etc. And when any item is selected AutoCompleteBox returns the selected value either Human instance or Animal instance via SelectedItem dependency property.
I want to achieve similar flexibility with my usercontrol. I wouild like to expose 2 dependency properties SuggestedItems and SelectedItem. Which ever collection is set to SuggestedItems via consumers of this usercontrol thru Binding, lets take as an example IEnumerable<Car>, the I want SelectedItem property to send instance of Car type back to consumer thru Binding. If I used IEnumerable<Boat>, then I need Boat to be returned with SelectedItem.
I was trying to achieve it by using below example using MVVM, but its not working. I am looking for some clues as to how it should be designed, Am I even on a correct path or I have to completely alter my design?
I created an UserControl called VehicleSelectorUserControl which has its own dedicated ViewModel called VehicleSelectorViewModel with two proerties SuggestedItems, SelectedItem.
And usercontrol has corresponding Dependency properties in its codebehind to expose them to consumers of usercontrol. UserControl XAML has a ListBox which is bound to SuggestedItems property of VehicleSelectorViewModel. When user makes a selection, VehicleSelectorViewModel SelectedItem is set, which them invokes a delegate called ItemSelected to notify VehicleSelectorUserControl codebehind, which then sets the SelectedItem Dependency property to make it available to consumer.
Below is code from the VehicleSelectorUserControl.xaml.cs code behind.
private VehicleSelectorViewModel _TheViewModel;
public UserNameControl()
{
InitializeComponent();
_TheViewModel = Resources["TheViewModel"] as VehicleSelectorViewModel;
_TheViewModel.ItemSelected = OnItemSelected;
}
public IEnumerable<object> SuggestedItems
{
get { return (IEnumerable<object>)GetValue(SuggestedItemsProperty); }
set { SetValue(SuggestedItemsProperty, value); }
}
public static readonly DependencyProperty SuggestedItemsProperty =
DependencyProperty.Register("SuggestedItems", typeof(IEnumerable<object>), typeof(VehicleSelectorControl), new PropertyMetadata(OnSuggestedItemsSet));
private static void OnSuggestedItemsSet(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
_TheViewModel.SuggestedItems = e.NewValue;
}
public object SelectedItem
{
get { return (String) GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(object), typeof(VehicleSelectorControl), null);
private void OnItemSelected()
{
SelectedItem = _TheViewModel.SelectedItem;
}
Its ViewModel VehicleSelectorViewModel code
public Action ItemSelected { get; set; }
private dynamic _SelectedItem;
public dynamic SelectedItem
{
get { return _SelectedItem; }
set
{
if (value != _SelectedItem)
{
_SelectedItem = value;
NotifyPropertyChanged("SelectedItem");
if(ItemSelected != null) ItemSelected.Invoke();
}
}
}
private dynamic _SuggestedItems;
public dynamic SuggestedItems
{
get { return _SuggestedItems; }
set
{
if (value != _SuggestedItems)
{
_SuggestedItems = value;
NotifyPropertyChanged("SuggestedItems");
}
}
}
The XAML of consumer will look like (Consumer has its own ViewModel, which responsible for supplying SuggestedCars [IEnumerable<Car>], SuggestedBoats [IEnumerable<Boat>].
<my:VehicleSelectorControl x:Name="MyCarSelectorControl"
SuggestedItems="{Binding SuggestedCars, Mode=TwoWay}"
SelectedItem="{Binding UserSelectedCar, Mode=TwoWay}" />
<my:VehicleSelectorControl x:Name="MyBoatSelectorControl"
SuggestedItems="{Binding SuggestedBoats, Mode=TwoWay}"
SelectedItem="{Binding UserSelectedBoat, Mode=TwoWay}" />
I try to adopt entity-level validation (attributes validation on properties on entities) by create ViewModel that expose that Entity.
public class MyViewModel
{
public MyEntity MyEntity { get; set; }
}
I set binding in xaml to, this xaml page set its DataContext to instance of MyViewModel
TextBlock Text="{Binding MyEntity.MyProperty}"
When I load MyEntity from database and set it to MyViewModel, nothing happen. I also call NotifyPropertyChanged("MyEntity"); and it still nothing happen.
I try again by create MyProperty in MyViewModel
public class MyViewModel
{
private MyEntity MyEntity { get; set; }
public string MyProperty
{
get { return this.MyEntity.MyProperty; }
set { this.MyEntity.MyProperty = value; }
}
}
And changed xaml to bind to MyProperty. This time when I call NotifyPropertyChanged("MyProperty "); View get update correctly, when I input incorrect data, it has ValidationErrors at MyEntity but View don't raise that error (not show red border)
I want to know how can I get entity-level validation working with MVVM.
Hi
you must change the definition of ViewModel such as
public class MyViewModel:IDataErrorInfo
{
}
and implement interface.
this force View to show red border on error.
wish to help.
How do i extend an existing control (ComboBox in my case) to include a new property which i can bind to a property on my view model??
I have a Dependancy Property on the control's class as follows:
public class MyComboBox : ComboBox
{
public static readonly DependencyProperty MyTextProperty =
DependencyProperty.Register("MyText", typeof(string), typeof(MyComboBox));
public string MyText
{
get
{
return GetValue(MyComboBox.MyTextProperty).ToString();
}
set
{
SetValue(MyComboBox.MyTextProperty, value);
}
}
And want to bind to it declaratively from XAML like this:
<MyComboBox MyText="{Binding MyTextOnViewModel,
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
The Binding just won't work, any ideas why??
Thanks.
Your getter and setter reference TestTextProperty while the property is declared as MyTextProperty.
Your getter should also be casting instead of calling .ToString()
return (string)GetValue(MyTextProperty);
See this page for more complete instructions.