DependencyProperty Strangeness - wpf

I've put together a WPF application using ObservableCollection and Dependency Properties which is cool because I just have to add an item to the ObservableCollection and it shows up automatically, e.g. I display the objects in the collection as boxes on the screen in a wrappanel, each box showing its Title.
So then I wanted to have each item show not only its Title plus a prefix or suffix, but the Dependency Object property doesn't even seem to be used. I can put a break point on it and it is never reached.
Can anyone enlighten me why, if I add text to my outgoing property, that that text is never seen? I have read that the value is actually "stored not in the object but in WPF" but I don't understand what that means.
Why is the text this text will NOT be seen never output by the dependency object?
public class ApplicationItem : DependencyObject
{
public string Title
{
get
{
return (string)GetValue("this text will NOT be seen: " + TitleProperty);
}
set
{
SetValue(TitleProperty, "this text will be seen: " + value);
}
}
}

TitleProperty is not a normal property but a dependency property so if you want to retrieve the value of your TitleProperty you have to do :
var title = (string)GetValue(TitleProperty);
In WPF guideline, the public property to access a Dependency Property is not called by WPF and the binding engine (not necessary). This public property is only used by your code behind. So you MUST not add code logic inside your public property.
But you can use a FrameworkPropertyMetadata when you register your DP and provide a CoerceValueCallback to change the setted value.
You can also use a IValueConverter with your binding.

I got this to work for me:
public string Title
{
get
{
string value = (string)GetValue(TitleProperty);
return value + " postfix";
}
set
{
SetValue(TitleProperty, "Prefix " + value);
}
}
Is there a reason why you are attempting to modify the value when retrieving it rather than just modifying it when the value is set?

Related

XAML, MVVM and TwoWay Data Binding

I have the following XAML. I'm using UWP (Universal Windows Platform) but the problem I'd like to describe also applies to other XAML frameworks such as WPF:
<!-- MyVM is a ViewModel -->
<AutoSuggestBox
QueryIcon="Find"
TextChanged="{x:Bind MyVM.FilterTextChanged}"
Text="{x:Bind MyVM.FilterText, Mode=TwoWay}"/>
This is how it works: when the user types text in the AutoSuggestBox, MyVM ViewModel is informed about every key stroke and filters data using FilterText.
This is how MyVM looks like:
// Uses INotifyPropertyChanged from MVVMLight
private string filterText;
public string FilterText
{
get { return filterText; }
set { Set(ref filterText, value); }
}
public async void FilterTextChanged()
{
await LoadData(); // uses FilterText to filter data
}
The problem arises when I need to modify FilterText value, for example to clear it or to set a pre-defined filter. Thanks to the TwoWay binding, the text in the AutoSuggestBox is displayed correctly but as a "side-effect" the FilterTextChanged method is called (because the text has changed). I don't want this "side-effect". It is bad from two reasons:
It makes the ViewModel dependent on XAML in the View. What I mean by that is that although I don't call FilterTextChanged, it is called anyway when I set the FilterText value just because it is TwoWay-bound in XAML.
It makes automated unit testing impossible. Without XAML, the ViewModel behaves differently: the FilterTextChanged method is not called when I set a FilterText value.
This is a general problem with XAML, MVVM, and TwoWay binding, not restricted to the specific example with AutoSuggestBox.
How to solve this problem? The main question for me is how to unit test it?
The problem arises when I need to modify FilterText value, for example to clear it or to set a pre-defined filter. Thanks to the TwoWay binding, the text in the AutoSuggestBox is displayed correctly but as a "side-effect" the FilterTextChanged method is called (because the text has changed). I don't want this "side-effect".
If I understand your question, you just do not want the FilterTextChanged called when you empty the AutoSuggestBox or set a default value for it. If so, you do not need to use TextChanged to trigger the FilterTextChanged method, you could call it when the property value changed.
For example:
<AutoSuggestBox QueryIcon="Find" Text="{x:Bind MyVM.FilterText, Mode=TwoWay}"/>
private string filterText;
public string FilterText
{
get { return filterText; }
set
{
if (value != string.Empty || value != "default value")
{
FilterTextChanged();
}
filterText = value;
RaisePropertyChanged("FilterText");
}
}
public async void FilterTextChanged()
{
await LoadData(); // uses FilterText to filter data
}

Changing a user control inside property outside it

I have a simple user control with a Ellipse and a TextBlock
I want to change the TextBlock.Text property inside the User Control from outside like this
I didnt find any simple explanation or way to do this
if there's any i would like to know
thanks
Expose it as a property:
public partial class MyUserControl : UserControl
{
public string Text
{
get { return MyTextBox.Text; }
set { MyTextBox.Text = value; }
}
}
If you want to set it via a binding, then you need to create a dependency property, but if you're just using literals and/or code-behind, then the above is fine.

how and where to use DependencyProperty

I am new to WPF so after reading for a while I deduce that my problem needs to be handled with this pattern: DependencyProperty.
I want my ToggleButton to have another boolean property.
My problem is where should I assign this property, and how? Inside the object that is bound to the ToggleButton?
Let's say I have a class cell (which is bound to this Button) that when clicked I want that from this point on, it would hold new face with trigger on.
My new property will be:
bool wasClick
Can someone explain to me how I should write it and tell me more about this new concept?
EDIT:
The main topic is where should I define it so I want it asoocited to a Button but where should I write the code. Lets say I have a class that is bound to a Button. Should I write:
public static readonly DependencyProperty IsSpinningProperty =
DependencyProperty.Register(
... "IsSpinning", typeof(Boolean),
in this class or should I write it in my view model? If so, where and how?
As the name implies (kind of poorly), a dependency property is a property whose value can depend on something else. Generally, this means a property whose value gets determined automatically (and dynamically) by the WPF framework under certain conditions. The most common conditions are:
The property has a default value, or inherits its value from an ancestor in the visual tree. In this case, the property's value is determined without it ever being set.
The property is the target of data binding.
The property's value is set by an animation.
Not all properties whose value gets set by the WPF framework need to be dependency properties. Any CLR property with a public getter and setter can be the source of a two-way data binding.
In your case, it sounds like you don't really need a dependency property, not if you're using a view model. You could just do this (assuming that you've implemented property-change notification in your class):
private bool _IsChecked;
public bool IsChecked
{
get { return _IsChecked; } }
set
{
if (value == _IsChecked)
{
return;
}
_IsChecked = value;
WasChecked = WasChecked || value;
OnPropertyChanged("IsChecked");
}
}
private bool _WasChecked;
public bool WasChecked
{
get { return _WasChecked; }
private set
{
if (value == _WasChecked)
{
return;
}
_WasChecked = value;
OnPropertyChanged("WasChecked");
}
}

DependencyProperty Orientation problem

I am learning WPF and am trying to create my first UserControl. My UserControl consists of
StackPanel
StackPanel contains a Label and TextBox
I am trying to create two Dependency Properties
Text for the Label
Orientation for the StackPanel - The orientation will affect the position of the Label and TextBox effectively
I have successfully created a Text dependency property and bind it to my UserControls . But when I created the Orientation property, I seem to get following error in get property
The as operator must be used with a reference type or nullable type ('System.Windows.Controls.Orientation' is a non-nullable value type)
public static DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(System.Windows.Controls.Orientation), typeof(MyControl), new PropertyMetadata((System.Windows.Controls.Orientation)(Orientation.Horizontal)));
public Orientation Orientation
{
get { return GetValue(OrientationProperty) as System.Windows.Controls.Orientation; }
set { SetValue(OrientationProperty, value); }
}
Appreciate your help.
Edit:
I changed the code as below and it seem to work as expected. But is this the correct way to solve the problem?
public Orientation Orientation
{
get
{
Orientation? o = GetValue(OrientationProperty) as System.Windows.Controls.Orientation?;
if (o.HasValue)
{
return (System.Windows.Controls.Orientation)o.Value;
}
else
{
return Orientation.Horizontal;
}
}
set { SetValue(OrientationProperty, value); }
}
The error message says it all. The as operator can only be used with a Type that is nullable (reference type, or Nullable<T>), because it will return either the value cast, or null.
What you're trying to use it on is an enumeration.
Just use a regular cast:
get { return (System.Windows.Controls.Orientation) GetValue(OrientationProperty); }
Reasons why:
You define a default value in your DependencyProperty.Register call, eliminating any default null value
Your DependencyProperty is typeof(Orientation), which doesn't allow for nulls
Your class's property definition is Orientation, which doesn't allow for nulls
Any attempt to set an invalid value via direct calls to SetValue(OrientationProperty, null) will receive an exception, so your property getter won't ever see a null value even by a naughty user of it.

WPF derived ComboBox SelectedValuePath issue

In our application we have a very large set of data that acts as our data dictionary for ComboBox lists, etc. This data is staticly cached and keyed off of 2 variables, so I thought it wise to write a control that derived from ComboBox and exposed the 2 keys as DPs. When those 2 keys have proper values I set the ItemsSource of the ComboBox automatically from the data dictionary list it corresponds to. I also automatically set the SelectedValuePath and DisplayMemberPath in the constructor to Code and Description, respectively.
Here's how an example of how an item in the ItemsSource from the data dictionary list always looks:
public class DataDictionaryItem
{
public string Code { get; set; }
public string Description { get; set; }
public string Code3 { get { return this.Code.Substring(0, 3); } }
}
The value of Code is always 4 characters long, but sometimes I only need to bind 3 characters of it. Hence, the Code3 property.
Here's how the code looks inside my custom combobox to set the ItemsSource:
private static void SetItemsSource(CustomComboBox combo)
{
if (string.IsNullOrEmpty(combo.Key1) || string.IsNullOrEmpty(combo.Key2))
{
combo.ItemsSource = null;
return;
}
List<DataDictionaryItem> list = GetDataDictionaryList(combo.Key1, combo.Key2);
combo.ItemsSource = list;
}
Now, my problem is, when I change the SelectedValuePath in the XAML to Code3, it doesn't work. What I bind to SelectedValue still gets the full 4 character Code from DataDictionaryItem. I even tried rerunning SetItemsSource when the SelectedValuePath was changed and no dice.
Can anyone see what I need to do to get my custom combobox to wake up and use the SelectedValuePath provided if it's overridden in the XAML? Tweaking the value in the property setter in my SelectedValue bound business object is not an option.
Here's how the XAML looks for my combobox in a form:
<c:CustomComboBox Key1="0" Key2="8099" SelectedValuePath="Code3" SelectedValue="{Binding Thing}"/>
EDIT: I just ran snoop on my code and it says my SelectedValuePath is Code... it doesn't appear to ever be set to Code3... Zuh?
Ok, I figured it out.
Apparently setting the default values of a DependencyProperty in the default non-static constructor of a WPF control is a no-no. So, at first I tried this:
static ValueCodeListComboBox()
{
SelectedValuePathProperty.OverrideMetadata(typeof(ValueCodeListComboBox), new PropertyMetadata("Code"));
DisplayMemberPathProperty.OverrideMetadata(typeof(ValueCodeListComboBox), new PropertyMetadata("Description"));
}
But this kept throwing an error saying:
Metadata override and base metadata must be of the same type or derived type.
Finally figured out that meant I needed to use FrameworkPropertyMetadata instead of PropertyMetadata:
static ValueCodeListComboBox()
{
SelectedValuePathProperty.OverrideMetadata(typeof(ValueCodeListComboBox), new FrameworkPropertyMetadata("Code"));
DisplayMemberPathProperty.OverrideMetadata(typeof(ValueCodeListComboBox), new FrameworkPropertyMetadata("Description"));
}
Now changing SelectedValuePath in the XAML works great.

Resources