I simply want to pass parameter to the control. But it threw error "Input string was not in a correct format." Why?* *
Xaml
<Views:SomeView SecurityId="abc"></Views:SomeView>
Model:
class Data
{
public string Case { get; set; }
public Data(int _input)
{
if (_input==1)
{
Case = "First";
}
else
{
Case = "Second";
}
}
}
ViewModel:
class DataViewModel
{
public string GetData
{
get
{
return D.Case;
}
set
{
D.Case = value;
}
}
public Data D;
public DataViewModel(string i)
{
D = new Data(Convert.ToInt16(i));
}
}
MainWindow
public partial class SomeView : UserControl
{
public string SecurityId
{
get
{
return (string)GetValue(SecurityIdProperty);
}
set { SetValue(SecurityIdProperty, value); }
}
public static readonly DependencyProperty
SecurityIdProperty =
DependencyProperty.Register("SecurityId",
typeof(string), typeof(SomeView),
new PropertyMetadata(""));
public SomeView()
{
DataContext = new DataViewModel(SecurityId);
InitializeComponent();
}
}
You never listened for changes.
You construct your DataViewModel with the value that SecurityId has at the time of the constructor call. Which is the default "". Then you change the value to "abc" through XAML. But that change is not transported anywhere. It happens and nobody cares. The construction of your DataViewModel is already done.
Do you want to listen to changes? I cannot tell. You will need to register a change handler for your dependency property.
In your PropertyMetaData you can pass a changed event handler as second parameter, for example a static method:
public static readonly DependencyProperty
SecurityIdProperty =
DependencyProperty.Register("SecurityId",
typeof(string), typeof(SomeView),
new PropertyMetadata("", new PropertyChangedCallback(MyValueChanged)));
You can then have a method to handle changes:
private static void MyValueChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
{
// react on changes here
}
It's not an attached property by the way. It's a normal dependency property.
This is because, you are trying to parse "abc" as integer, but you are not handling exceptions caused by ConvertTo.Int16() method.
Write DataViewModel constructor like,
public DataViewModel(string i)
{
int value = 0;
int.TryParse(i, out value); // TryParse handles the exception itself.
D = new Data(value);
}
Related
Let say I have classes like those:
public class ParentModel : INotifyPropertyChanged
{
// INotifyPropertyChanged pattern implemented ...
public IChildViewModel CurrentControlModel {
get { ... } set { /* Notify on changes */ }
}
}
public class ChildModelA : INotifyPropertyChanged, IChildViewModel
{
// INotifyPropertyChanged pattern implemented ...
public ICommand Command {
get { ... } set { /* Notify on changes */ }
}
}
public class ChildModelB : INotifyPropertyChanged, IChildViewModel
{
// INotifyPropertyChanged pattern implemented ...
public ICommand Command {
get { ... } set { /* Notify on changes */ }
}
}
public class ButtonViewModel : INotifyPropertyChanged
{
ICommand Command get { ... } set { /* Notify on changes */ }
}
I would like to have Command property to reflect the value of parentModelInstance.CurrentControlModel.Command event if
CurrentControlModel changes.
I cannot modify the ButtonViewModel.Command property to be a proxy of the property
because it's the view model for all buttons and I don't want to specialize it for every possible button.
If I do
ButtonViewModel viewModel;
viewModel.Command = parentModelInstance.CurrentControlModel.Command;
it doesn't work because CurrentControlModel can change (it's null at startup for instance).
I can listen to PropertyChanged event but it will cumbersome to do that for all properties of the model.
Any easier and cleaner alternative ?
Context
To give a bit of context, it's part of a dynamic toolbar code where you have buttons that can change icon, be disabled or change command, command target etc...
depending on what is the current focused control (which can be of different type).
CurrentControlModel is the view model of the current focused control.
The journey into the binding land
First solution: One helper to rule them all and with the View Model bind them
It was inspired by ReactiveUI and manual binding on DependencyProperty :
public static BindableProperty<TProperty> Watch<TInstance, TProperty>(
this TInstance instance,
Expression<Func<TInstance, TProperty>> expression,
BindingMode mode = BindingMode.TwoWay)
{
return new BindableProperty<TProperty>(instance,
GetPath((MemberExpression)expression.Body), mode);
}
public static void BindTo<TInstance, TProperty>(
this BindableProperty<TProperty> bindable,
TInstance instance,
Expression<Func<TInstance, TProperty>> expression) where TInstance
: DependencyObject
{
var getterBody = expression.Body;
var propertyInfo = (PropertyInfo)((MemberExpression)getterBody).Member;
var name = propertyInfo.Name;
var dependencyPropertyName = name + "Property";
var fieldInfo = typeof(TInstance).GetField(dependencyPropertyName,
BindingFlags.Public | BindingFlags.Static);
var dependencyProperty = (DependencyProperty)fieldInfo.GetValue(null);
Binding binding = new Binding();
binding.Source = bindable.Source;
binding.Path = new PropertyPath(bindable.Path);
binding.Mode = bindable.Mode;
binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(instance, dependencyProperty, binding);
}
public class BindableProperty<T>
{
public object Source { get; }
public string Path { get; }
public BindingMode Mode { get; }
public BindableProperty(object source, string path, BindingMode mode)
{
Source = source;
Path = path;
Mode = mode;
}
}
ButtonViewModel must derive from DependencyObject and implement the pattern
for the Command property
public class ButtonViewModel : DependencyObject
{
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand),
typeof(ButtonViewModel), new PropertyMetadata(default(ICommand)));
public ICommand Command
{
get { return (ICommand) GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
}
Then it can be used like this (for binding paste command to the paste button):
container.Watch(x => x.CurrentControlModel.Commands.Paste)
.BindTo(pasteButtonViewModel, x => x.Command);
Issues
Must setup DependencyProperty pattern for all of properties of view model.
Reflexion and expression analysis can raise runtime exceptions.
In case a conversion is needed, we must write a proxy doing the conversion and the value modification propagation.
Second solution: Reactive.UI and Fody
Reference the ReactiveUI.WPF and ReactiveUI.Fody, and modify the view model like this
public class ButtonViewModel : ReactiveObject
{
[Reactive]
public ICommand Command { get; set; }
}
Then we can bind the two properties like this:
container.WhenAnyValue(x => x.CurrentControlModel.Commands.Paste)
.BindTo(pasteButtonViewModel, x => x.Command);
Potential issue remaining
By not relying on DependencyProperty (apparently), there is a potential issue because we cannot tell the listener that the property is not set (with DependencyProperty.UnsetValue).
it's a one way binding.
I have a user control in WPF which I want to pass a list of items to when I use the user control in another window (the user control contains a ComboBox and label and has some important functionality). This works fine if I have a single control on my page, but if I have two I get the values listed from both my user controls, presumably because the DependencyProperty is static. I can't remove the static as it throws an error when registering the dependency property.
public static readonly DependencyProperty ComboBoxValuesProperty =
DependencyProperty.Register("ComboBoxValues", typeof(ObservableCollection<ComboBoxValue>), typeof(SystemConfigComboBox),
new PropertyMetadata(new ObservableCollection<ComboBoxValue>()));
public ObservableCollection<ComboBoxValue> ComboBoxValues
{
get { return GetValue(ComboBoxValuesProperty) as ObservableCollection<ComboBoxValue>; }
set { SetValue(ComboBoxValuesProperty, value); }
}
Below shows both user controls ComboBoxes containing Value1 and Value2
<customEditors:SystemConfigComboBox SystemConfigEntry="Entry1" ComboBoxLabel="Combo 1" ComboBoxWidth="300">
<customEditors:SystemConfigComboBox.ComboBoxValues>
<customEditors:ComboBoxValue DisplayValue="Value1" ActualValue="VALUE1" />
</customEditors:SystemConfigComboBox.ComboBoxValues>
</customEditors:SystemConfigComboBox>
<customEditors:SystemConfigComboBox SystemConfigEntry="Entry2" ComboBoxLabel="Combo 2" ComboBoxWidth="300">
<customEditors:SystemConfigComboBox.ComboBoxValues>
<customEditors:ComboBoxValue DisplayValue="Value2" ActualValue="VALUE2" />
</customEditors:SystemConfigComboBox.ComboBoxValues>
</customEditors:SystemConfigComboBox>
and just for information the ComboBoxValue class:-
public class ComboBoxValue : FrameworkElement
{
public static readonly DependencyProperty DisplayValueProperty =
DependencyProperty.Register("DisplayValue", typeof(string), typeof(ComboBoxValue));
public static readonly DependencyProperty ActualValueProperty =
DependencyProperty.Register("ActualValue", typeof(string), typeof(ComboBoxValue));
public string DisplayValue
{
get { return (string)GetValue(DisplayValueProperty); }
set { SetValue(NameProperty, value); }
}
public string ActualValue
{
get { return (string)GetValue(ActualValueProperty); }
set { SetValue(ActualValueProperty, value); }
}
}
You should initialize the ObservableCollection in the constructor of the control class and not in the DependencyProperty.Register method:
public class SystemConfigComboBox
{
public SystemConfigComboBox()
{
ComboBoxValues = new ObservableCollection<ComboBoxValue>();
}
public static readonly DependencyProperty ComboBoxValuesProperty =
DependencyProperty.Register("ComboBoxValues", typeof(ObservableCollection<ComboBoxValue>), typeof(SystemConfigComboBox)));
...
}
Please refer to MSDN for more information about this: https://msdn.microsoft.com/en-us/library/aa970563(v=vs.110).aspx
Subject. This is happens at the moment when i'm closing application, and in section of CloseAsync() of my ViewModel trying to save attached model, that is inherited from SavableModelBase.
My ViewModel:
public ServerTabViewModel(Server server)
{
Argument.IsNotNull(() => server);
Server = server;
}
#region Properties
[Model]
public Server Server
{
get { return GetValue<Server>(ServerProperty); }
set { SetValue(ServerProperty, value); }
}
public static readonly PropertyData ServerProperty = RegisterProperty("Server", typeof(Server));
[ViewModelToModel("Server")]
public string ServerIpAddress
{
get { return GetValue<string>(ServerIpAddressProperty); }
set { SetValue(ServerIpAddressProperty, value); }
}
public static readonly PropertyData ServerIpAddressProperty = RegisterProperty("ServerIpAddress", typeof(string));
...
#endregion
protected override async Task CloseAsync()
{
var server = new Server
{
ServerIpAddress = ServerIpAddress, // ServerIpAddress is null now and model property (Server.ServerIpAddress) too.
...
};
SettingsService.SaveServer(server);
}
My Model:
public class Server : SavableModelBase<Server>
{
public string ServerIpAddress
{
get { return GetValue<string>(ServerIpAddressProperty); }
set { SetValue(ServerIpAddressProperty, value); }
}
public static readonly PropertyData ServerIpAddressProperty = RegisterProperty("ServerIpAddress", typeof(string));
...
}
In case, if i remove attribute [ViewModelToModel("Server")] on ServerIpAddress property of my ViewModel, value is available. It is predictable - no longer due on the property with a model.
How can I get the model does not set their properties to null at the moment when i'm closing my application? And why this happens?
I don't have a lot of code to go by, but I think this is caused by the views being unloaded. This means that your vm will be canceled (not saved since there is no explicit save) and your models will be reset to reset to the original state.
You can change this behavior by setting a property on the Model attribute:
[Model(SupportIEditableObject = false)]
public Server Server
{
...
}
I am new to WPF and this is my first post. I have created a class called 'Fruit' that descends from 'DependencyObject' and adds and extra property called 'Apple'. I have created a new custom control that includes a Dependency Property called 'MyFruit' of type 'Fruit'. My question is, how can i set the default value for the properties within 'MyFruit' object (i.e. the 'Apple' property? I would like to set this in XAML using the object.
public class Gauge : Control
{
.
.
.
//---------------------------------------------------------------------
#region MyFruit Dependency Property
public Fruit MyFruit
{
get { return (Fruit)GetValue(MyFruitProperty); }
set { SetValue(MyFruitProperty, value); }
}
public static readonly DependencyProperty MyFruitProperty =
DependencyProperty.Register("MyFruit", typeof(Fruit), typeof(CircularGauge), null);
#endregion
}
//-------------------------------------------------------------------------
#region Fruit class
public class Fruit : DependencyObject
{
private int apple;
public int Apple
{
get { return apple; }
set { apple = value; }
}
}
#endregion
Instead of null in your dependency property metadata insert
new UIPropertyMetadata("YOUR DEFAULT VALUE GOES HERE")
So now it becomes
public static readonly DependencyProperty MyFruitProperty =
DependencyProperty.Register("MyFruit", typeof(Fruit), typeof(CircularGauge), new UIPropertyMetadata("YOUR DEFAULT VALUE GOES HERE"));
You need to use PropertyMetaData like this:
class MyValidation
{
public bool status
{
get { return (bool)GetValue(statusProperty); }
set { SetValue(statusProperty, value); }
}
public static readonly DependencyProperty statusProperty = DependencyProperty.Register("status", typeof(bool), typeof(MyValidation),new PropertyMetadata(false));
}
I have a string dependency property (SearchText), when updated, needs to update a collection dependency property (Results).
My collection dp:
public IEnumerable<string> Results{
get { return (IEnumerable<string>) GetValue(ResultsProperty); }
set { SetValue(ResultsProperty, value); }
}
public static readonly DependencyProperty ResultsProperty=
DependencyProperty.Register("Results", typeof(IEnumerable<string>), typeof(MainWindowVM), new UIPropertyMetadata(new List<string>()));
I tried this with no luck. i put a breakpoint at the Results = .... line and it never got hit.
public string SearchText{
get { return (string) GetValue(SearchTextProperty); }
set {
Results =
from T in Tree.GetPeople(value)
select T.FullName;
SetValue(SearchTextProperty, value);
}
}
public static readonly DependencyProperty SearchTextProperty=
DependencyProperty.Register("SearchText", typeof(string), typeof(MainWindowVM), new UIPropertyMetadata(""));
XAML:
<TextBox DockPanel.Dock="Top" Text="{Binding SearchValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<ListBox DockPanel.Dock="Top" ItemsSource="{Binding NameResults}" SelectedItem="{Binding Search}" />
When setting a dependency property in XAML or through a binding, the runtime will always bypass the instance property alias and directly call GetValue and SetValue. It is because of this that your instance setter isn't being invoked.
What you may wish to consider doing is registering a method with the dependency property which will be invoked when the property changes. This is most easily done when creating the PropertyMetadata for the dependency property.
I believe the following example does what you're looking to do. In the example, my class has two depencency properties aliased as First and Second. When I set the value for First, my change handler is invoked and I set the value of Second.
public class DependencyPropertyTest : DependencyObject
{
public static readonly DependencyProperty FirstProperty;
public static readonly DependencyProperty SecondProperty;
static DependencyPropertyTest()
{
FirstProperty = DependencyProperty.Register("FirstProperty",
typeof(bool),
typeof(DependencyPropertyTest),
new PropertyMetadata(false, FirstPropertyChanged));
SecondProperty = DependencyProperty.Register("SecondProperty",
typeof(string),
typeof(DependencyPropertyTest),
new PropertyMetadata(null));
} // End constructor
private bool First
{
get { return (bool)this.GetValue(FirstProperty); }
set { this.SetValue(FirstProperty, value); }
} // End property First
private string Second
{
get { return (string)this.GetValue(SecondProperty); }
set { this.SetValue(SecondProperty, value); }
} // End property Second
private static void FirstPropertyChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs ea)
{
DependencyPropertyTest instance = dependencyObject as DependencyPropertyTest;
if (instance == null)
{
return;
}
instance.Second = String.Format("First is {0}.", ((bool)ea.NewValue).ToString());
} // End method FirstPropertyChanged
} // End class DependencyPropertyTest
I hope that helps.
As I commented, the currently accepted answer is correct in the principle, except that it MUST use the SetCurrentValue method instead of doing a simple assignment. See this answer for more explanation about it.
Here is the same code with this fix:
public class DependencyPropertyTest : DependencyObject
{
public static readonly DependencyProperty FirstProperty;
public static readonly DependencyProperty SecondProperty;
static DependencyPropertyTest()
{
FirstProperty = DependencyProperty.Register("FirstProperty",
typeof(bool),
typeof(DependencyPropertyTest),
new PropertyMetadata(false, FirstPropertyChanged));
SecondProperty = DependencyProperty.Register("SecondProperty",
typeof(string),
typeof(DependencyPropertyTest),
new PropertyMetadata(null));
} // End constructor
private bool First
{
get { return (bool)this.GetValue(FirstProperty); }
set { this.SetValue(FirstProperty, value); }
} // End property First
private string Second
{
get { return (string)this.GetValue(SecondProperty); }
set { this.SetValue(SecondProperty, value); }
} // End property Second
private static void FirstPropertyChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs ea)
{
DependencyPropertyTest instance = dependencyObject as DependencyPropertyTest;
if (instance == null)
{
return;
}
// SetCurrentValue should be used here!
instance.SetCurrentValue(SecondProperty,
String.Format("First is {0}.", ((bool)ea.NewValue).ToString());
} // End method FirstPropertyChanged
} // End class DependencyPropertyTest
The Order that you put on it your dependency property is very important
the fist one put in the class file, will be executed fist then the one down to it
class A {
dependecy 1
dependecy 2
dependecy 3
}
it will be called in the order set on the class, so just order your dependency on the order you want
if 1 depend on 2 -> put 2 first, then 1 like this
class A {
dependecy 2
dependecy 1
dependecy 3
}