I have two options on different dialogs (classes) related to CheckBox in different XAML files:
First pair:
C#:
public class FirstClass : DependencyObject
{
public static readonly DependencyProperty testProperty =
DependencyProperty.Register("testProperty", typeof(bool),
typeof(FirstClass),
new UIPropertyMetadata(false));
public bool testProperty
{
get { return (bool)this.GetValue(testProperty); }
set { this.SetValue(testProperty, value); }
}
}
XAML:
<CheckBox IsChecked="{Binding Path=testProperty, Mode=TwoWay}">
Second pair:
C#
public class SecondClass : DependencyObject
{
public static readonly DependencyProperty testProperty =
FirstClass.testProperty.AddOwner(typeof(SecondClass));
public bool testProperty
{
get { return (bool)this.GetValue(testProperty); }
set { this.SetValue(testProperty, value); }
}
}
XAML:
<CheckBox IsChecked="{Binding Path=testProperty, Mode=TwoWay}">
I want to bind the option from the first dialog to the option from the second dialog (A<=>B). If the CheckBox in the first dialog is checked, the CheckBox in the second dialog should also be checked. Should I use ApplicationSettings for this purpose?
DataBinding is not only the process of binding between two dependency properties, but also between one CLR and one dependency property. In fact it usually is the most common binding scenario. So what you can do is use a model object which stores the value of your ui elements and reuse it in another view or another control.
First of all i advise you not to use a DependencyObject derived object as your data holder, while its still valid it has it cons aswell.
First our data object, which stores just our data. Please lookup how to implement the INotifyPropertyChanged interface, because i left the implementation for readability.
class DataHolder : INotifyPropertyChanged
{
public bool MyValue
{
get{return mMyValue;}
set{mMyValue = value; RaiseProperty("MyValue");}
}
private bool mMyValue;
}
This object can now be easily bound to your ui elements with the help of the UI DataContext. Its vital to know that the DataContext is an inherited dependency property, which means that in the tree of controls, if an element doesn't have a set datacontext it automatically gets the datacontext of his parent. Think of a login dialog with user name and password. If you have a simple model with two properties like user name and password, you just set this model as the datacontext to the dialog and all controls can bind to these properties. Back to your example, you just set an instance of DataHolder to your window datacontext property
public MainWindow()
{
InitializeComponents();
var model = new DataHolder();
DataContext = model;
}
and now you can use bindings in your xaml like this
<CheckBox IsChecked="{Binding MyValue, Mode=TwoWay}"/>
Multiple controls can bind to the same property, and you can use the same model when you open another view.
One last advice, you should read the DataBinding chapter of the msdn and its a good idea to look into the MVVM pattern because its widely used with WPF to great success. We use it for a rather large application and are very satisfied with it.
Related
I am rather new to MVVC/wpf, having mostly worked with winforms.
What I want to accomplish is dynamic databinding without using code behind in WPF. The user interface consists of a devexpress grid and a couple of buttons. Each button press loads an object list and presents the objects in the grid. The lists contain different object types depending on the button pressed. For this example I have two classes to present: FatCat and FatDog.
In winforms this works:
private void button1_Click(object sender, EventArgs e)
{
((GridView)gridCtrl.MainView).Columns.Clear();
gridCtrl.DataSource = new BindingSource(itsModel.GetAll<FatDog>(), null);
}
private void button2_Click(object sender, EventArgs e)
{
((GridView)gridCtrl.MainView).Columns.Clear();
gridCtrl.DataSource = new BindingSource(itsModel.GetAll<FatCat>(), null);
}
I have configured the grid to create columns dynamically, so everything just works. itsModel is of type CatClientModel.
In wpf I have defined the DataContext to be CatClientModel.
What should I use for ItemsSource in the grid to achieve the same behaviour as my winforms solution?
dxg:GridControl ItemsSource="{Binding SomeDynamicList}"
In other words, what should SomeDynamicList be in the code above? Or am I going about this the wrong way?
I am, as I stated, using the DevExpress wpf grid control, but the question ought to be general and apply to any control presenting object lists.
In other words, what should SomeDynamicList be in the code above?
SomeDynamicList should be an ObservableCollection<T> property to which you can add any objects of type T that you want to display in the GridControl.
Set the DataContext of the GridControl, or any of its parent elements, to an instance of a class where this property is defined:
public class CatClientModel
{
public ObservableCollection<Animal> SomeDynamicList { get; } = new ObservableCollection<Animal>();
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new CatClientModel();
}
}
Ok. But the thing is that the ObservableCollection contains different types. Unfortunately there is no feasible class to inherit from. I want to bind to either ObservableCollection or ObservableCollection depending on which button was pressed
Switch the DataContext then, or change the property into an IEnumerable and set it to a new collection each time the button is clicked. This requires you to implement the INotifyPropertyChanged interface in your view model
private System.Collections.IEnumerable _collection;
public System.Collections.IEnumerable MyProperty
{
get { return _collection; }
set { _collection = value; OnPropertyChanged(); }
}
If you want to use XAML to define which data sources your code maps to for each grid that is possible. That does require at least some method of MVVM manager either prism or mvvmlight to connect the view model to the view.
so if you do go the MVVM model route, the Model would contain a description for each of your grids like this:
public BulkObservableCollection<icd10facet> FacetList
{
get { return this._facets; }
set { SetProperty(ref this._facets, value); }
}
public INotifyTaskCompletion<BulkObservableCollection<PetsConvert>> ConceptList
{
get { return this._concept; }
set
{
SetProperty(ref this._concept, value);
}
}
In the XAML for your code the grid woud bind to the grid defined by ConceptList in this way:
ItemsSource="{Binding ConceptList.Result}"
this answer does NOT address how to wire up Prism 6.0 for example to use a view model but for examples see:
https://github.com/PrismLibrary/Prism
Which contains documentation and starter code. Keep in mind that there is not any specific reason that putting code in the code behind for the view is a problem, first solve the problem and then refactor if separation of concerns is an issue for you.
Using this technique you can bind each grid to its own data source. In the MVVM space buttons and other things use a commanding model to communicate with the view model.
<Button Content="Load Rule Data" Width="100" Height="40" HorizontalAlignment="Left" Margin="5px" Command="{Binding LoadRuleData }"/>
this requires defining a command delegate in the viewmodel for LoadRuleData
public DelegateCommand LoadRuleData { get; private set; }
and then (usually in the constructor) wire the DelegateCommand to the method that is going to do the work.
this.LoadRuleData = new DelegateCommand(this.loadRules);
I am trying to solve this issue for so many hours:
I have user custom control of grid named NewMazeGrid and I want to use it as a control in MainWindow. MainWindow contains MazeViewModel(mazeVM member).
I'm trying to set the values of the grid, when the property MazeViewModel:MySingleplay changes.
(I'm using the INotifyPropertyChanged for it, and it works perfectly fine. I guess, the problem is in the final binding)
The code:
This is the property MazeViewModel:MySingleplay getter:
public string MySingleplay
{
get
{
if (myModel.MySingleplay == null)
{
return "";
} else
{
return myModel.MySingleplay.ToString();//works perfect
}
}
}
this is the NewMazeGrid.xaml.cs:
namespace VisualClient.View.controls
{
public partial class NewMazeGrid : UserControl
{
private MazePresentation myMaze;
private string order; //dont really use it
//Register Dependency Property
public static readonly DependencyProperty orderDependency =
DependencyProperty.Register("Order", typeof(string), typeof(NewMazeGrid));
public NewMazeGrid()
{
myMaze = new MazePresentation();
InitializeComponent();
DataContext = this;
lst.ItemsSource = myMaze.MazePuzzleLists;
}
public string Order
{
get
{
return (string)GetValue(orderDependency);
}
set
{
SetValue(orderDependency, value);
myMaze.setPresentation(value); //(parsing string into matrix)
}
}
}
}
this is the MainWindow.xaml.cs:
public partial class MainWindow : Window
{
private MazeViewModel mazeVM;
public MainWindow()
{
InitializeComponent();
mazeVM = new MazeViewModel(new ClientMazeModel(new TCPClientConnection()));
DataContext = mazeVM;
mazeVM.connectToServer();
}
private void bu_Click(object sender, RoutedEventArgs e)
{
bool isC = mazeVM.isConnected();
mazeVM.openSingleplayGame("NewMaze");//works perfect
}
this is the MainWindow.xaml:
<Window x:Class="VisualClient.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Controls ="clr-namespace:VisualClient.View.controls"
xmlns:vm ="clr-namespace:VisualClient.ViewModel"
xmlns:local="clr-namespace:VisualClient.View"
mc:Ignorable="d"
Title="Main Window" Height="350" Width="525" MinWidth="900" MinHeight="600">
<WrapPanel >
<Button Name ="bu" Content="Click_Me" Click="bu_Click"/>
<Grid Name="myGrid">
<Controls:NewMazeGrid Order="{Binding MySingleplay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</WrapPanel>
</Window>
I get this error on the binding line: Value cannot be null.
To sum:
It initialize fine the window in the ctor, but when the property changes it does not get into the Order property setter. therefor my grid never changes.
What should be the right syntax for binding in this case? how do I bind it to the right property?
Folders hierarchy explorer
WPF may not call the CLR wrapper of a dependency property, but just directly call the GetValue and SetValue methods of the underlying DependencyObject. This is why there should not be any logic except the GetValue and SetValue calls.
This is explained in XAML Loading and Dependency Properties:
Because the current WPF implementation of the XAML processor behavior
for property setting bypasses the wrappers entirely, you should not
put any additional logic into the set definitions of the wrapper for
your custom dependency property. If you put such logic in the set
definition, then the logic will not be executed when the property is
set in XAML rather than in code.
Similarly, other aspects of the XAML processor that obtain property
values from XAML processing also use GetValue rather than using the
wrapper. Therefore, you should also avoid any additional
implementation in the get definition beyond the GetValue call.
To get notified about property value changes, you can register a PropertyChangedCallback by property metadata. Note also that there is a naming convention for DependencyProperty fields. Yours should be called OrderProperty:
public static readonly DependencyProperty OrderProperty =
DependencyProperty.Register(
"Order", typeof(string), typeof(NewMazeGrid),
new PropertyMetadata(OnOrderChanged));
public string Order
{
get { return (string)GetValue(OrderProperty); }
set { SetValue(OrderProperty, value); }
}
private static void OnOrderChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
((NewMazeGrid)obj).myMaze.setPresentation((string)e.NewValue);
}
Besides that, you must not set
DataContext = this;
in the constructor of NewMazeGrid. This effectively prevents inheriting the DataContext from the parent window, so that {Binding MySingleplay} won't work. Except under special circumstances you should never explicitly set a UserControl's DataContext.
So, remove the DataContext assignment from the constructor:
public NewMazeGrid()
{
myMaze = new MazePresentation();
InitializeComponent();
lst.ItemsSource = myMaze.MazePuzzleLists;
}
That said, there is also no need to set UpdateSourceTrigger=PropertyChanged on a one-way binding. It only has an effect in two-way (or one-way-to-source) bindings:
<Controls:NewMazeGrid Order="{Binding MySingleplay}"/>
I have UserControl A given below with two Radio Buttons.
This UserControl view has its ViewModel.
Question:
I again have two Views Create and Edit.
I want to use the above mentioned UserControl within Create/Edit with requirement that i can make the radiobuttons or any of the elements in UserControl to be Visible or Hidden based on the requirement in Create/Edit View.
Eg: Create May not require Radio button 1 and 2.So only Rectangle must be displayed.
Whatever input i give in the list or textbox must be updated in UserControl's ViewModel and the search result after clicking on button must be sent to Create/Edit accordingly.
Note:Create/Edit have their own ViewModels.Please suggest which approach is best considering MVVM
The Control has to be placed in the grayed out area as shown in rectangle for Create/Edit View
You can create DependancyProperty inside your UserControl like
public static readonly DependencyProperty RadioButtonVisibilityProperty=
DependencyProperty.Register( "RadioButtonVisibility", typeof(Visibility),
typeof(MyUserControl));
public Visibility RadioButtonVisibility
{
get { return (Visibility)GetValue(RadioButtonVisibilityProperty); }
set { SetValue(RadioButtonVisibilityProperty, value); }
}
and inside your UserControl's xaml Set the radiobutton's visibility like
<RadioButton Visibility="{Binding Parent.RadioButtonVisibility,ElementName=LayoutRoot}"/>
and in your main View(Create/Edit) do like this
<MyUserControl x:Name="Edit" RadioButtonVisibility="Visible"/>
or
<MyUserControl x:Name="Create" RadioButtonVisibility="Hidden"/>
And dont forget to give your UserControl's parent Grid the name "LayoutRoot"
like
<Grid x:Name="LayoutRoot"/>
It might be a good idea to have the UserControl be driven by some abstract BaseViewModel. Then you create two sub-classes EditViewModel and CreateViewModel which you then use based on the context.
Quick crude example for the radio buttons:
public abstract class BaseViewModel
{
public bool ShowRadioButtons { get; protected set; }
}
public class EditViewModel : BaseViewModel
{
public EditViewModel()
{
ShowRadioButtons = true;
}
}
public class CreateViewModel : BaseViewModel
{
public CreateViewModel()
{
ShowRadioButtons = false;
}
}
I have a UserControl with a Dependency Property:
public static readonly DependencyProperty Step2CommandProperty =
DependencyProperty.Register("Step2Command", typeof(ICommand), typeof(MyTripNavigationStep), new PropertyMetadata(null));
public ICommand Step3Command
{
get { return (ICommand)GetValue(Step3CommandProperty); }
set { SetValue(Step3CommandProperty, value); }
}
Then I have a ViewModel with a ICommand property:
public ICommand SaveStep1Command
{
get
{
return new RelayCommand(() =>
{
});
}
}
Then I Bind the two properties like this in the Page where i have the viewModel as the DataContext and the UserControl.
<UserControls:Step Step3Command="{Binding SaveStep1Command, Mode=OneWay}" />
The binding is not being applied, and the Step3Command in the userControl always appears to be null.
I know that the DataContext is working fine, and that Visual Studio does not allow me to put TwoWay binding. I'm using GalaSoft Simple Mvvm and Visual Studio CTP update 2.
Anybody has a clue of what am I doing wrong? thanks.
You defined the property wrong. The get block gets called everytime the property is accessed, so everytime you (or the MVVM magic in WPF) accesses SaveStep1Command a new command is created. This is not what you want.
Rewrite the property like this:
In your constructor code, write:
SaveStep1Command = new RelayCommand(...)
And define your property just like this:
public ICommand SaveStep1Command { get; }
If you are on an older version of .net / C#, you must define it like this:
public ICommand SaveStep1Command { get; private set; }
Explanation attempt: It might be that the databinding only creates weak references. With your way of defining the SaveStep1Command it is created once the binding is set up, and then it just "lies around" on the heap - and when the GC kicks in next time, the space is freed up because it has no strong references.
Yes. The problem is that you are not handling the Changed event of the DP.
Reference this: https://blog.jerrynixon.com/2013/07/solved-two-way-binding-inside-user.html
So here is the problem I'm beating my head against: I have a custom user control that exposes two dependency properties that are bound to my ViewModel. In my ViewModel I have an instance of a class that holds multiple properties that express values that relate to the user control as well as to items that control manipulates. Here's a bit of sample code to explain it visually so here is a simple sample of my control, it's a Slider that is combined with a checkbox that allows the user to lock the slider.
<custom:SliderControl IsLocked="{Binding Path=CustomClass.IsLocked, Mode=TwoWay}" SliderValue="{Binding Path=CustomClass.Value, Mode=TwoWay}" />
IsLocked and SliderValue are dependency properties that effectively manipulate the checkbox and slider that are contained in the custom control. All of the control functions work as intended, except for the bindings to the class I've defined. If I create individual properties, as in one int property and one bool property the bindings work as intended. However I have five sliders, and each slider in my actual code has five properties that tie in to them. I'm trying to eliminate code duplication by creating a class to hold these properties in a reusable object shrinking my 25 properties down to 5 class instances.
My CustomClass inherits ObservableObject and has a bool property and int property named IsLocked and SliderValue respectively. For more visual aids here is what it looks like:
public class CustomClass : ObservableObject
{
public const string SliderValuePropertyName = "SliderValue";
private int _sliderValue= 0;
public int SliderValue
{
get
{
return _sliderValue;
}
set
{
if (_sliderValue== value)
{
return;
}
_sliderValue= value;
RaisePropertyChanged(SliderValuePropertyName );
}
}
public const string IsCheckedPropertyName = "IsChecked";
private bool _isChecked = false;
public bool IsChecked
{
get
{
return _isChecked;
}
set
{
if (_isChecked == value)
{
return;
}
_isChecked = value;
RaisePropertyChanged(IsCheckedPropertyName);
}
}
The ViewModel property is very similar and looks like this, an new instance of the class is created when the ViewModel loads:
public const string SliderOnePropertyName = "SliderOne";
private CustomClass _sliderOne;
public CustomClass SliderOne
{
get
{
return _sliderOne;
}
set
{
if (_sliderOne== value)
{
return;
}
_sliderOne= value;
RaisePropertyChanged(SliderOnePropertyName );
}
}
Why won't the updating of the dependency property that is bound to the property in the class update properly? Is it because you can't properly update the class instance property by itself and instead have to update the entire class instance whenever changes occur? Or do I need to further customize the setter in this ViewModel property? As it sits now changing the slider value or checkbox never hits the bound property at all and nothing errors out when debugging.
EDIT: I've also surrounded the control in a Border and set the Border UIElement's DataContext to that of the class and then subsequently applied the more simple path binding to the underlying custom control. This however did not have any effect on my problem.
I'm a homegrown programmer so I often miss things when putting code together and I'm guessing this is the case here, unless what I'm trying just won't work.
Any help would be greatly appreciated.
EDIT: So I've been toying around with using a custom event that will let me know when the specific property of the custom control changes and then having that event wired up in my ViewModel to update the existing class. This works but still creates code duplication as now I have to have 10 events, 2 events per control, one to check for when the value of the slider changes and the other to detect when the checkbox IsChecked value changes. This code duplication exists since you can't route multiple command parameters (like a simple string identifier for which slider is being manipulated as well as the value you want to use in the code). This limitation means I can't just use 2 events that differentiate between which control is undergoing changes within the defined method as exposing the physical control to the ViewModel breaks the MVVM pattern. Using a class as the datacontext for the user control made it so I didn't care what control was being manipulated as they each had their own class instance. Using events this unravels the MVVM pattern as now I need to know which of the five controls is being manipulated by the user.
It can't be this hard to use a class in property bindings. I have to be missing something remedial.
here is a full example:
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
public class ViewModel
{
public SliderValues slv { get; private set; }
public ViewModel()
{
slv = new SliderValues();
}
}
public class SliderValues : INotifyPropertyChanged
{
bool _isLocked = false;
public bool IsLocked
{
get { return _isLocked; }
set
{
_isLocked = value;
OnPropertyChanged("IsLocked");
}
}
int _theValue = 5;
public int TheValue
{
get { return _theValue; }
set
{
_theValue = value;
OnPropertyChanged("TheValue");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string prop)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
Now the xaml:
<UserControl x:Class="TestBindings.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Slider Height="23" HorizontalAlignment="Left" Margin="114,138,0,0" Name="slider1" VerticalAlignment="Top" Width="100"
DataContext="{Binding slv}" Value="{Binding TheValue, Mode=TwoWay}"/>
</Grid>
</UserControl>
May be there is just a syntactical error. Try this
{Binding Path=CustomClass.IsLocked, Mode=TwoWay}
Try this...<custom:SliderControl DataContext="{Binding CustomClass}" IsLocked="{Binding IsLocked, Mode=TwoWay}" SliderValue="{Binding Value, Mode=TwoWay}" />