I need to create a DependancyProperty Collection
and somehow bind or be able to know when each item changes
It is not easy to explain this problem..
To declare a simple DependancyProperty we do that:
public static readonly DependencyProperty SelectedColorProperty =
DependencyProperty.Register("SelectedColor", typeof(Color),
typeof(MyClass), new PropertyMetadata(Colors.Red));
public Color SelectedColor
{
get { return (Color)GetValue(SelectedColorProperty); }
set { SetValue(SelectedColorProperty, value); }
}
And later on in XAML we can do that:
<MyClass SelectedColor="{Binding blah blah}"/>
If I add a simple callback on value change in the static declaration so instead of
just new PropertyMetadata(Colors.Red)
i'll have: new PropertyMetaData(Colors.Red,MyCallback)
Each time the value is changed I will be able to react to that change from code behind.
Ok, now what I want is something that will be written in XAML like that:
<MyClass>
<MyClass.SelectedColors>
<Color>{Binding blah1}</Color>
<Color>{Binding blah2}</Color>
<Color>{Binding blahN}</Color>
<MyClass.SelectedColors>
</MyClass>
However, I can't use multibinding as I want to know which color has
changed and multibinding gives me an aggregator tactic, meaning when
I done writing the multibinding converter I will get all the values
from all "blahs" at the same time and will not be able to know which
one was the one that changed..
I began writing something simple that look as follows:
public static readonly DependencyProperty SelectedColorsProperty =
DependencyProperty.Register("SelectedColors", typeof(List<Color>),
typeof(MyClass),
new PropertyMetadata(new List<Color>()));
public List<Color> SelectedColors
{
get { return (List<Color>)GetValue(SelectedColorsProperty); }
set { SetValue(SelectedColorsProperty, value); }
}
public MyClass()
{
foreach(var item in SelectedColors)
{
//dunno what goes here.. or how to bind the color change?
}
}
So now I can't create a callback that will handle each change of each item, right?
Yet that is exactly what I need, to be able to react to a change in each item..
However it does allow me to express the XAML syntax I need..
Any ideas? Did anyone bumped into that kind of problem before?
I noticed very short explanation in MSDN but i'm not sure if it helps me and how
to use it if can help, it is the last paragraph in the page that mentions
something about using a
Freezable<T>
http://msdn.microsoft.com/en-us/library/aa970563.aspx
I appreciate your help,
Thanks.
Update: A MultiBinding Solution can also be ok if it will allow to distinguish which of the values had the change (in our example which color changed).
The Markup in that case might look like that:
<MyClass>
<MyClass.SelectedColors>
<MultiBinding..
<Binding Path="FillColor" ElementName="MyRectWhichHasFillColorDProperty"/>
<Binding blah2 which is Color2 />
<Binding blah3 which is ColorN />
Wrap your Colors in a class that implements INotifyPropertyChanged and then setup a listener to the PropertyChanged event for each color in your constructor.
Wrapper class
public class ColorViewModel : INotifyPropertyChanged
{
private Color color;
public Color Color
{
get { return color; }
set
{
if (value != color)
{
color = value;
NotifyPropertyChanged("Color");
}
}
}
// INotifyPropertyChanged implementation goes here
}
Then in your event listener you will get the individual ColorViewModel that fired the property changed event.
I have had to solve a very similar problem before and I created an ObservableCollection of my ViewModel class so that I could subscribe to an collection changes and attach/detach my event listeners to the changed items.
I typed this from memory on my Surface so there are probably lots of typos and you should take the code sample as sudo code.
Hope this helps.
EDIT
Here is what your Xaml could look like:
<namespace:MyClass>
<namespace:MyClass.SelectedColors>
<namespace:ColorViewModel Color="{Binding SomeValue}" />
<namespace:ColorViewModel Color="{Binding OtherValue}" />
</namespace:myClass.SelectedColors>
</namespace:MyClass>
where Colors is a collection of ColorViewModel.
You will not be able to wire up your PropertyChanged listeners in your constructor since at the time of construction there are no ColorViewModels in Colors, so I would just make Colors an ObservableCollection and use the CollectionChanged event to wire up PropertyChanged listeners to any new additions to the collection.
You may just want to Use a SolidColorBrush instead of a ColorViewModel class as it has a Color property and a Changed event.
Related
I want to change a value (textBlock) according to an event. Then, I want to refresh my window, but I couldn't. I used invalidateVisual as well as solutions of other posts, but nothing worked.
Thank you in advance
Several solutions (the first and second one does not make use of databinding).
txtMyControl.text = "New value";
If not on the main thread, you could use the dispatcher to update the value.
Application.Current.Dispatcher.BeginInvoke(() => txtMyControl.text == "New Value")
However, the most WPF friendly way to do it is to use the databinding.
Any change made to the value in code will be instantly reflected in the UI.
XAML
<TextBox x:Name="txtExample" Text="{Binding MyTextProperty,Mode=TwoWay}" Height="24" Width="120"/>
In your code, you have to declare a variable that will be persistent.
private ExampleModel _ExampleModel = new ExmampleModel();
When you load your code, you associate that variable to your textbox data context.
txtExample.DataContext = _ExampleModel
Then, you have the class that will contains all the editable properties on screen (textboxes, radio boxes, etc...)
public class ExampleModel : INotifyPropertyChanged
{
private string _MyTextProperty = "test";
public string MyTextProperty {
get { return _MyTextProperty; }
set {
if (string.Compare(_MyTextProperty, value) != 0) {
_MyTextProperty = value;
RaisePropertyChanged("MyTextProperty");
}
}
}
public void RaisePropertyChanged(string PropertyName)
{
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
public event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged;
public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);
}
Whenever you handle your event, you just have to change the value of the property containing the information and the UI will refresh accordingly. Also, since we use a two-way binding, the value from your textbox will always be the same than the one contained by MyTextProperty property in ExampleModel class, which make value retrieval very easy.
ex:
_ExampleModel.MyTextProperty = "New value";
If you were already using databinding, make sure the class used implements INotifyPropertyChanged and that the propertyChanged event is called when the property value change or otherwise it won't update the UI.
The best approach to what you're trying to do would be to use Data Binding.
You need to have a string object that will always hold the value of your textblock. Next you need to bind that object to your textblock and then use the event provided by the INotifyPropertyChanged interface and each time the value changes its representation (the textblock) will change to, no need to refresh the window.
More information here
If your event updates the textblock and the textblock you are using is bound to a string property and that property issues a NotifyPropertyChanged() in it's set method, that will cause the display to refresh as you desire.
There are other ways, but this is the easiest given my understanding of your question.
(this is similar to the other answer, but I tried to word so it is easier to understand/implement.)
I'm new to wpf, as the title suggests. I was using wpf as it was winforms (what's all that binding non sense anyway) until, of course, I tried it and got blown away.
So I was digging into user controls and dependency properties. I read that, in order to get the ui to remain in sync with what's under the hood you need to use observable collections, notifypropertychanged / changing and dependency properties for the stuff that you use.
My question is this:
Let's say I have this dep. prop. for a user control (type of Media.Color) :
public Color Color
{
get { return (Color)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}
The xaml uses it for a binding, it works, all is good. But, when it gets updated, I would like to do something with it in code.
So I tried putting a Console.writeline("fired") like so:
public Color Color
{
get { return (Color)GetValue(ColorProperty); }
set {
Console.WriteLine("Fired");
SetValue(ColorProperty, value);
}
}
No dice. Could someone please enlighten me how this stuff works? I am obviously missing something (just the other day someone on stack told me about MouseCapture so... ).
Thank you for your time.
Edit
http://www.wpftutorial.net/DependencyProperties.html
Basically it says, in big bold letters,
Important: Do not add any logic to these properties, because they are
only called when you set the property from code. If you set the
property from XAML the SetValue() method is called directly.
If you are using Visual Studio, you can type propdp and hit 2x tab to
create a dependency property.
And goes on to explain why and how you should proceed.
Solution
So, I tried what #Krishna suggested, and my user control crashed and burned.
Here was my dep prop. (as it was before asking this question).
public static readonly DependencyProperty ColorProperty = DependencyProperty.Register("Color", typeof(Color), typeof(ColorPickerMaster), new PropertyMetadata(default(Color)));
Turns out the problem was using
(...) new Prop.Metadata(null, OnPropChanged)
Using
public static readonly DependencyProperty ColorProperty = DependencyProperty.Register("Color", typeof(Color), typeof(ColorPickerMaster), new PropertyMetadata(OnColorChanged));
private static void OnColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Console.WriteLine(e.NewValue);
}
yields a beautiful win.
Thank you for your time and answers.
When it comes to DependencyProperties you use property changed callback to track changes to your property like below example. And then you use e.NewValue and e.OldValue to write your logic. More about DependencyProperties on MSDN
public Color color
{
get { return (Color)GetValue(colorProperty); }
set { SetValue(colorProperty, value); }
}
public static readonly DependencyProperty colorProperty =
DependencyProperty.Register("color", typeof(Color), typeof(YourClass), new PropertyMetadata(null,colorChanged));
private static void colorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
YourClass c = d as YourClass;
if(c!=null)
{
}
}
From MSDN - XAML Loading and Dependency Properties:
The current WPF implementation of its XAML processor is inherently dependency property aware. The WPF XAML processor uses property system methods for dependency properties when loading binary XAML and processing attributes that are dependency properties. This effectively bypasses the property wrappers. When you implement custom dependency properties, you must account for this behavior and should avoid placing any other code in your property wrapper other than the property system methods GetValue and SetValue.
If you want to add custom logic in a setter you will have to make it a simple field (not a DependecyProperty) implement INotifyPropertyChanged and bind to it.
I created a control, derived from Canvas, that should plot a live diagram, given values that are passed via a binding to a DependencyProperty. The simplified version is this:
public class Plotter : Canvas
{
public float Value { get { return (float)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } }
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(float), typeof(Plotter),
new PropertyMetadata(0f, new PropertyChangedCallback(ValueChangedCallBack)));
public static void ValueChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)
{
Plotter plotter = (Plotter)property;
plotter.Value = (float)args.NewValue; //<-- Removed this line to get it to work
// Actually draw the value into the canvas geometry
plotter.PlotValue(plotter.Value);
}
}
I bound the control like this:
<mystuff:Plotter Value="{Binding MyViewModelProperty}" Height="50" Width="200" />
My ViewModel implements INotifyPropertyChanged and calls PropertyChanged correctly. If I bind MyViewModelProperty to a textbox, it correctly updates every time. Only if I bind it to my own control, my ValueChangedCallBack is only called once as the page is loaded, and then never again.
What am I not seeing here? Thanks for any help!
Solved: I dont have to set the Value explicitly in the callback.
You set the property Value on the callback for the property Value changing. That doesn't make much sense in any case. But is that locally set value overriding the binding value, causing your binding to no longer be set on the dependency property?
Do you need to set the Mode of your binding to TwoWay?
Should you not be using DependencyProperty.Register instead of DependencyProperty.RegisterAttached?
What I need is to be able to execute code in a code-behind for my view class when a property on my view-model is updated. My understanding is that I need to use a dependency-property.
My view-model does implement INotifyPropertyChanged.
Here is the property in my view-model:
private DisplayPosition statusPosition;
public DisplayPosition StatusPosition
{
get { return this.statusPosition; }
set
{
this.statusPosition = value;
this.OnPropertyChanged("StatusPosition");
}
}
Here is my dependency property in my view:
public DisplayPosition StatusPosition
{
get { return (DisplayPosition)GetValue(StatusPositionProperty); }
set { SetValue(StatusPositionProperty, value); }
}
public static readonly DependencyProperty StatusPositionProperty =
DependencyProperty.Register(
"StatusPosition",
typeof(DisplayPosition),
typeof(TranscriptView),
new PropertyMetadata(DisplayPosition.BottomLeft));
Here is where I set up my binding in my view class (handler for this.DataContextChanged):
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
Binding myBinding = new Binding("StatusPosition");
myBinding.Source = this.DataContext;
myBinding.NotifyOnTargetUpdated = true;
this.SetBinding(TranscriptView.StatusPositionProperty, myBinding);
}
When I put a break-point on the setter for the property in my view, it never gets hit even after I watch the value change in the view-model, and the PropertyChanged event raised. Ultimately, my goal is to be able to put more code in the setter.
The hairy detail, if you're curious, is that I need to move a TextBlock around between multiple StackPanels based on this value. I can't seem to find a XAML-only way of doing that.
More often than not, these problems are simple little obvious things that I've missed. Nothing I'm trying is helping me sort this one out, though.
When I put a break-point on the setter for the property in my view, it never gets hit even after I watch the value change in the view-model, and the PropertyChanged event raised. Ultimately, my goal is to be able to put more code in the setter.
You can't do this. When you're using DependencyProperties, the setter is never called when the bound property changes. It's only purpose is to allow you to set the DP from code.
You need to, instead, add a PropertyChangedCallback to the metadata on your DP, and add the extra code there. This will get called when the DP value updates, whether via binding, code, etc.
Here are the relevant parts of the XAML file:
xmlns:local="clr-namespace:BindingTest"
<ListBox x:Name="myList"
ItemsSource="{Binding Source={x:Static local:MyClass.Dic},
Path=Keys,
Mode=OneWay,
UpdateSourceTrigger=Explicit}">
</ListBox>
MyClass is a public static class and Dic is a static public property, a Dictionary.
At a certain point I add items to the Dictionary and would like the ListBox to reflect the changes.
This is the code I thought about using but it doesn't work:
BindingExpression binding;
binding = myList.GetBindingExpression(ListBox.ItemsSourceProperty);
binding.UpdateTarget();
This code instead works:
myList.ItemsSource = null;
myList.ItemsSource = MyClass.dic.Keys;
I would prefer to use UpdateTarget, but I can't get it to work.
What am I doing wrong?
Binding of items is handled rather differently than standard binding of DependencyPropertys in WPF (specifically, by ItemsControls).
I think you want something like the following:
var itemsView = CollectionViewSource.GetDefaultView(myListBox.ItemsSource);
itemsView.Refresh()
It is in fact the ICollectionView object that you want to refresh. This effectively is the object that manages the collection binding for you. See the MSDN page for more info.
Expanding on the previous answer by Noldorin, you can hook to the PropertyChanged event to make this happen automatically anytime your model is updated:
public partial class YourView {
public YourView () {
InitializeComponent();
ViewModel=YourDataObject;
ViewModel.PropertyChanged+=OnUpdateYourDataObject;
}
public YourDataObject? ViewModel {
get => DataContext as YourDataObject;
set => DataContext=value;
}
public void OnUpdateYourDataObject(object? sender, PropertyChangedEventArgs? e) =>
CollectionViewSource.GetDefaultView(YourListBox.ItemsSource).Refresh();
}
Replace YourDataObject and YourListBox with the correct values. This will automatically update your view whenever a property on your object changes. I'm not sure why this isn't handled automatically but I couldn't figure out the setting to make that work.