Binding DataContext loose DependencyProperty - c#

I have a main WPF window which will host a bunch of UserControls.
I want Every UC to have there own logic so I added a VM like this :
UC Constructor
public IfcUpdaterViewModel IfcUpdaterViewModel;
public IfcUpdaterView()
{
IfcUpdaterViewModel = new IfcUpdaterViewModel();
DataContext = IfcUpdaterViewModel;
InitializeComponent();
}
And I added a ICommand DP to bind it in the main form like this :
UC Dependency
public static readonly DependencyProperty UpdateCommandProperty =
DependencyProperty.Register("UpdateCommand", typeof(ICommand), typeof(IfcUpdaterView), new UIPropertyMetadata());
/// <summary>
/// Dependency to bind the update command
/// </summary>
public ICommand UpdateCommand
{
get => (ICommand) GetValue(UpdateCommandProperty);
set => SetValue(UpdateCommandProperty, value);
}
the binding is set with this :
MainForm
public void ToDocument()
{
_ifcUpdater = new IfcUpdaterView();
MainPresenter.Content = _ifcUpdater;
_ifcUpdater.SetBinding(IfcUpdaterView.UpdateCommandProperty, new Binding(nameof(MainDataContext.UpdateIfcFile))
{
Mode = BindingMode.OneWay,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
});
}
But with this the command binding isn't working. but if I remove the DataContext the binding working fine but I loose my VM logic.
Is there a way to combine both ?

Related

ReactiveUI : WPF Two way binding not working as expected

I am working on a WPF application using reactiveui, and am having difficulty getting two way binding working in my custom dependency property. I can get this working using WPF binding but not using the reactiveui.
I have a user control called IPAddressControl, which takes in an IP address input and provides a dependency property ValidIpAddress, shown below.
public static readonly DependencyProperty ValidIpAddressProperty =
DependencyProperty.Register(
"ValidIpAddress",
typeof(bool),
typeof(IPAddressControl),
new FrameworkPropertyMetadata(
false,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(OnValidIpAddressPropertyChanged)));
public bool ValidIpAddress
{
get
{
return (bool)GetValue(ValidIpAddressProperty);
}
set
{
SetValue(ValidIpAddressProperty, value);
}
}
private static void OnValidIpAddressPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
IPAddressControl IPAddressControl = d as IPAddressControl;
IPAddressControl.ValidIpAddress = (bool)e.NewValue;
}
In my view, I set up the binding as follows :
public partial class MyView : ReactiveUserControl<MyViewModel>
public MyView()
{
InitializeComponent();
ViewModel = new MyViewModel();
this.WhenActivated(view =>
{
this.Bind(ViewModel, vm => vm.ValidIpAddress, v => v.ipaddress.ValidIpAddress);
});
}
And define my view model property
public class MyViewModel : ReactiveObject
{
bool _validIpAddress = false;
public bool ValidIpAddress
{
get => this._validIpAddress;
set => this.RaiseAndSetIfChanged(ref _validIpAddress, value);
}
}
When the property gets updated in IPAddressControl
IPAddressControl.ValidIPaddress = true;
I would expect MyViewModel.ValidIpAddress.Set to be called, however this is not happening.
When I revert to the standard WPF implementation, the two way binding works well, i.e. I set the binding in the myview.xaml
ValidIpAddress="{Binding Path=ValidIpAddress}"
then set the data context in constructor MyView();
this.DataContext = new MyViewModel()
So it appears I am doing something wrong in relation to the reactiveui binding.
I'd appreciate if someone could help.
Thanks.

Multi stage databinding with a custom usercontrol

I have a custom control with a ViewModel. In this control I make programatically binding from the properties of the control to the ViewModel.
When I use the control and I make a binding to the property the value's aren't updated. I have the this for the bindings
In the customControl ViewModel
private string _InitValue;
public string InitValue
{
get { return _InitValue; }
set { _InitValue = value; NotifyPropertyChanged();}
}
In the customControl I set the binding
initValueBinding = new Binding();
initValueBinding.Source = LocalDataContext;
initValueBinding.Path = new PropertyPath("InitValue");
initValueBinding.Mode = BindingMode.OneWayToSource;
initValueBinding.BindsDirectlyToSource = true;
initValueBinding.UpdateSourceTrigger = UpdateSourceTrigger.Default;
BindingOperations.SetBinding(this, PlusMinControl.InitValueProperty, initValueBinding);
The InitValueProperty is a dependency property.
public static DependencyProperty InitValueProperty = DependencyProperty.Register(nameof(InitValue), typeof(string), typeof(PlusMinControl), new PropertyMetadata( new PropertyChangedCallback(test)) );
private static void test(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
;
}
public string InitValue
{
get { return ((string)(base.GetValue(PlusMinControl.InitValueProperty))); }
set { base.SetValue(PlusMinControl.InitValueProperty, value); }
}
For the implementation of the customControl
<plm:PlusMinControl InitValue="{Binding InitVal}" />
In the code behind I set the datacontext and I've InitVal defined as a normal property.
When I debug the code I can trace the changes till the PropertyChangedCallback but the property in the viewmodel isn't updated.
Can anyone tell me what I do wrong? And how I should fix this.
Thank you!
you have inverted and mixed same init :
this makes the binding property=viewmodel
<plm:PlusMinControl InitValue="{Binding InitVal}" />
and this too
"initValueBinding = new Binding();....."
this must be enough, remove the binding code
<plm:PlusMinControl InitialValue ="{Binding InitValue}" />
change the property to InitialValue in the control
change BindingMode.OneWayToSource to BindingMode.TwoWay

Store bindingexpression for later use

I have an application where IsEnabled binding is used.
I also need to remove this binding for a short moment.
I have created an ControlBehavior with an attached property to use as a temporary storageplace. ClearIsEnabledBinding will set the binding used in this attached property. RestoreIsEnabeldBinding is supposed to return the binding to the original place. This doesn't work. When the binding is cleared the attached property also is cleared.
The scenario is like this. I have a textbox with a binding for IsEnabled to the viewmodel with a converter. When I use a specific function all IsEnabled should be true regardless of the value in the viewmodel. This is easy just delete the binding and set to true. But when I return from this function I need to restore the binding to it's original binding to the viewmodel with a converter. So I need to save the entire bindingexpression somewhere and then "put" it back
My class is as follow:
Any suggestions to what I'm doing wrong?
public partial class ControlBehavior
{
public static readonly DependencyProperty IsEnabledBindingProperty = DependencyProperty.RegisterAttached(
"IsEnabledBinding",
typeof(BindingExpression),
typeof(ControlBehavior),
new PropertyMetadata(null));
public static void SetIsEnabledBinding(DependencyObject element, BindingExpression value)
{
if (value != null)
{
element.SetValue(IsEnabledBindingProperty, value);
SetIsEnabledBindingSet(element, true);
}
}
public static BindingExpression GetIsEnabledBinding(DependencyObject element)
{
var obj = element.GetValue(IsEnabledBindingProperty);
return (BindingExpression) obj;
}
public static readonly DependencyProperty IsEnabledBindingSetProperty = DependencyProperty.RegisterAttached(
"IsEnabledBindingSet",
typeof(bool),
typeof(ControlBehavior),
new PropertyMetadata(false));
public static void SetIsEnabledBindingSet(DependencyObject element, bool value)
{
element.SetValue(IsEnabledBindingSetProperty, value);
}
public static bool GetIsEnabledBindingSet(DependencyObject element)
{
return (bool)element.GetValue(IsEnabledBindingSetProperty);
}
public static void ClearIsEnabledBinding(DependencyObject element)
{
SetIsEnabledBinding(element, ((Control)element).GetBindingExpression(UIElement.IsEnabledProperty));
((Control)element).SetBinding(UIElement.IsEnabledProperty, new Binding());
}
public static void RestoreIsEnabledBinding(DependencyObject element)
{
if (!GetIsEnabledBindingSet(element))
{
return;
}
((Control)element).SetBinding(UIElement.IsEnabledProperty, GetIsEnabledBinding(element).ParentBindingBase);
}
}
We've used the below class to do what I think you're trying to achieve:
/// <summary>
/// Utilities to be used with common data binding issues
/// </summary>
class DataBindingUtils
{
private static readonly DependencyProperty DummyProperty = DependencyProperty.RegisterAttached(
"Dummy",
typeof(Object),
typeof(DependencyObject),
new UIPropertyMetadata(null));
/// <summary>
/// Get a binding expression source value (using the PropertyPath), from MSDN forums:
/// "Yes, there is one (class to provide the value) inside WPF stack, but it's an internal class"
/// Get source value of the expression by creating new expression with the same
/// source and Path, attaching this expression to attached property of type object
/// and finally getting the value of the attached property
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
public static Object Eval(BindingExpression expression)
{
// The path might be null in case of expression such as: MyProperty={Binding} - in such case the value we'll get
// will be the DataContext
string path = null;
if (expression.ParentBinding != null && expression.ParentBinding.Path != null)
{
path = expression.ParentBinding.Path.Path;
}
Binding binding = new Binding(path);
binding.Source = expression.DataItem;
DependencyObject dummyDO = new DependencyObject();
BindingOperations.SetBinding(dummyDO, DummyProperty, binding);
object bindingSource = dummyDO.GetValue(DummyProperty);
BindingOperations.ClearBinding(dummyDO, DummyProperty);
return bindingSource;
}
}
THe scenario is like this. I have a textbox with a binding for IsEnabled to the viewmodel with a converter. When I use a specific function all IsEnabled should be true regardless of the value in the viewmodel. This is easy just delete the binding and set to true. But when I return from this function I need to restore the binding to it's original binding to the viewmodel with a converter
Instead of dealing with binding you can provide mechanism to override IsEnabled value temporarily. I assume 2 thins: 1) you are binding to something (lets call it IsEnabled) 2) you are making some kind of edit mode, which should override binding value temproarily:
bool _isEditMode;
public bool IsEditMode
{
get { return _isEditMode; }
set
{
_isEditMode = value;
OnPropertyChanged(nameof(IsEnabled)); // update IsEnabled when IsEditMode changed
}
}
bool _isEnabled;
public bool IsEnabled
{
get
{
return IsEditMode || _isEnabled; // return true if IsEditMode == true or actual value otherwise
}
set
{
_isEnabled = value;
OnPropertyChanged();
}
}
Now if you bind
<TextBox IsEnable="{Binding IsEnabled, Converter= ...}" ... />
then as soon as you set IsEditMode = true textbox will become enabled. If you set IsEditMode = false textbox will be again enabled or disabled depending on the value of IsEnabled viewmodel property.

Setting up datacontext of the user control in client application in WPF

I have created a third party user control and now want to use it in a client application. I am having a problem with setting the DataContext to the control.
UserControl :-
<Grid>
<DataGrid x:Name="dataGrid" Width="400" Height="400" ItemsSource="{Binding DataTableSource}"/>
</Grid>
Code behind:-
public partial class CustomGridControl : UserControl
{
public CustomGridControl()
{
InitializeComponent();
this.DataContext = this;
}
public DataTable DataTableSource
{
get
{
return (DataTable)GetValue(GridSource);
}
set
{
SetValue(GridSource, value);
}
}
public static readonly DependencyProperty GridSource = DependencyProperty.Register("DataTableSource", typeof(DataTable), typeof(CustomGridControl), new PropertyMetadata(null));
}
How do I set DataTableSource in the client application?
<Grid>
<controls:CustomGridControl Name="myCustGrid" />
</Grid>
public MainWindow()
{
InitializeComponent();
ds = provider.GetDataSet();
table = ds.Tables[0];
//I have to set the table as DataTableSource. Here I am unable to access DataTableSource.
}
I am unable to access myCustGrid.DataTableSource. It says CustomGridControl does not contain a definition for DataTableSource. Why?
I've tried your custom as inheriting from Grid:
public partial class CustomGridControl : Grid
{
public DataTable DataTableSource
{
get
{
return (DataTable)GetValue(GridSource);
}
set
{
SetValue(GridSource, value);
}
}
public static readonly DependencyProperty GridSource = DependencyProperty.Register("DataTableSource", typeof(DataTable), typeof(CustomGridControl), new PropertyMetadata(null));
}
This the xaml:
<local:CustomGridControl x:Name="testCustomGrid" />
And i am able to use from codebehing like this:
testCustomGrid.DataTableSource = new DataTable("dtName");
I've been able to inherit from a UserControl too:
/// <summary>
/// Interaction logic for CustomGridControl.xaml
/// </summary>
public partial class CustomGridControl : UserControl, INotifyPropertyChanged
{
public CustomGridControl()
{
InitializeComponent();
}
private DataTable _DataTableSource;
public DataTable DataTableSource
{
get { return _DataTableSource; }
set
{
_DataTableSource = value;
PropertyChanged(this, new PropertyChangedEventArgs("DataTableSource"));
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public DataTable DataTableSourceVersion2
{
get { return (DataTable)GetValue(DataTableSourceVersion2Property); }
set { SetValue(DataTableSourceVersion2Property, value); }
}
// Using a DependencyProperty as the backing store for DataTableSourceVersion2. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataTableSourceVersion2Property =
DependencyProperty.Register("DataTableSourceVersion2", typeof(DataTable), typeof(CustomGridControl), new PropertyMetadata(null));
}
And this is the XAML:
<local:CustomGridControl DataTableSource="" DataTableSourceVersion2=""/>
So, both version of the DataSourceTable are available.

Silverlight detect when Dependency property has a Xaml binding

Is it possible to detect when a Xaml binding is set on a DependencyProperty in Silverlight?
E.g. if I had a custom user-control with single dependency property and a binding declared like this:
public class MyControl : UserControl
{
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register("Test",
typeof(object), typeof(MyControl),
new PropertyMetadata(null));
public object Test
{
get { return GetValue(TestProperty); }
set { SetValue(TestProperty, value); }
}
}
<MyControl Test="{Binding APropInViewModel}>
</MyControl>
Can I in the MyControl code to something like this?
// Ctor
public MyControl()
{
TestProperty.BindingChanged += new EventHandler(...)
}
e.g. can I get a notification of binding?
NOTE:
This is to solve a tricky order of precedence problem, described here so just checking for new values in the DependencyPropertyChanged handler won't work - because the property changed handler doesn't fire!!
It is possible for value changes in this binding. You can detect changes using propertychanged callback method which is static for Dependency properties.
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register("Test",
typeof(object), typeof(MyControl),
new PropertyMetadata(null, TestChangedCallbackHandler));
private static void TestChangedCallbackHandler(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
MyControl obj = sender as MyControl;
Test = args.NewValue;
}
However, this might cause following event listening cases. If you want to listen changes on this dependency property is explained in this link :
Listen DepencenyProperty value changes
public void RegisterForNotification(string propertyName, FrameworkElement element, PropertyChangedCallback callback)
{
Binding b = new Binding(propertyName) { Source = element };
var prop = System.Windows.DependencyProperty.RegisterAttached(
"ListenAttached" + propertyName,
typeof(object),
typeof(UserControl),
new System.Windows.PropertyMetadata(callback));
element.SetBinding(prop, b);
}
and call like this
this.RegisterForNotification("Test", this, TestChangedCallback);

Categories