Setting a custom control's member's property using a dependency property - c#

I have created a custom user control that is basically built of a grid, an image, and a textblock. I want to be able to set the source of the image and the text of the text block from properties of the user control. I have set up the following dependency properties; however, I want to know if there is a better way to set the child element's properties than the way below.
public sealed partial class LabeledTile : UserControl
{
public LabeledTile()
{
InitializeComponent();
}
public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(LabeledTile), new PropertyMetadata(null,(o, args) => ((LabeledTile) o).ImageSourceChanged((ImageSource)args.NewValue)));
private void ImageSourceChanged(ImageSource newValue)
{
ImageElement.Source = newValue;
}
public ImageSource ImageSource
{
get { return (ImageSource) GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(String), typeof(LabeledTile), new PropertyMetadata(null, (o, args) => ((LabeledTile)o).TextChanged((String)args.NewValue)));
private void TextChanged(string newValue)
{
TextElement.Text = newValue;
}
public String Text
{
get { return (String) GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
}
This is my first time using a dependency property and I want to make sure I am on the track. It seems like more work than should be required. Also, are callbacks really the best way to set the value?
Thank you in advance for the help.

Related

How to update one dependency property from another in UWP

I have two dependency properties and when value in First changes, I would like to update also the Second - for example give it temporary value and update UI, but I don't want to break the binding on Second property.
public bool Frist
{
get { return (bool)GetValue(FristProperty); }
set { SetValue(FristProperty, value); }
}
public static readonly DependencyProperty FristProperty =
DependencyProperty.Register("Frist", typeof(bool), typeof(ItemControl), new PropertyMetadata(false, FirstUpdated));
private static void FirstUpdated(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var theControl = d as ItemsControl;
// I would like to update also second dependency property here
// for example if First == true, then Second = Overriden
// else Second = the value from binding
}
public string Second
{
get { return (string)GetValue(SecondProperty); }
set { SetValue(SecondProperty, value); }
}
public static readonly DependencyProperty SecondProperty =
DependencyProperty.Register("Second", typeof(string), typeof(ItemsControl), new PropertyMetadata(string.Empty));
I've taken a look at various samples from WPF, but seems like there are missing things in UWP. What are the options?

Why to register DP as Attached property and then not implement a wrapper for it (WPF)

following an answer from #sky-dev here i could implement a solution to the airspace problem in WPF when working with a WinFormsHost Control.
But i'm new to WPF and now i'm trying to understand his code, and i don't see why some dependency properties are defined as attached properties and then their wrappers are implemented as for normal dependency properties.
I'll try to better explain my confusion with code:
First, the dependency properties are defined:
public class AirspacePopup : Popup
{
public static readonly DependencyProperty IsTopmostProperty =
DependencyProperty.Register("IsTopmost",
typeof(bool),
typeof(AirspacePopup),
new FrameworkPropertyMetadata(false, OnIsTopmostChanged));
public static readonly DependencyProperty FollowPlacementTargetProperty =
DependencyProperty.RegisterAttached("FollowPlacementTarget",
typeof(bool),
typeof(AirspacePopup),
new UIPropertyMetadata(false));
public static readonly DependencyProperty AllowOutsideScreenPlacementProperty =
DependencyProperty.RegisterAttached("AllowOutsideScreenPlacement",
typeof(bool),
typeof(AirspacePopup),
new UIPropertyMetadata(false));
public static readonly DependencyProperty ParentWindowProperty =
DependencyProperty.RegisterAttached("ParentWindow",
typeof(Window),
typeof(AirspacePopup),
new UIPropertyMetadata(null, ParentWindowPropertyChanged));
As you can see, the first DP is defined as a normal DP, as it is registered with the DependencyProperty.Register(..) method, but the rest of DP are defined as Attached, because they are registered with the DependencyProperty.RegisterAttached(..) method.
Now the wrappers:
public bool IsTopmost
{
get { return (bool)GetValue(IsTopmostProperty); }
set { SetValue(IsTopmostProperty, value); }
}
public bool FollowPlacementTarget
{
get { return (bool)GetValue(FollowPlacementTargetProperty); }
set { SetValue(FollowPlacementTargetProperty, value); }
}
public bool AllowOutsideScreenPlacement
{
get { return (bool)GetValue(AllowOutsideScreenPlacementProperty); }
set { SetValue(AllowOutsideScreenPlacementProperty, value); }
}
public Window ParentWindow
{
get { return (Window)GetValue(ParentWindowProperty); }
set { SetValue(ParentWindowProperty, value); }
}
All the wrappers are implemented for normal DP... As far as i now, if a DP is registered as attached, the wrapper should be something like:
public static bool GetFollowPlacementTarget(UIElement element)
{
if (element == null)
{
throw new ArgumentNullException(...);
}
return (bool)element.GetValue(FollowPlacementTarget);
}
public static void SetFollowPlacementTarget(UIElement element, bool value)
{
if (element == null)
{
throw new ArgumentNullException(...);
}
element.SetValue(FollowPlacementTarget, value);
}
So what could be the reason to register a dependency property as an attached property when it hasn't a wrapper to use it as attached property at XAML? Could someone please explain why this code is implemented like that?
Thank you.

Changing standard property into a DependencyProperty

In developing some UserControls for internal use I followed this exmaple from MSDN http://msdn.microsoft.com/en-us/library/vstudio/ee712573(v=vs.100).aspx
The public value of one control is used by another control. The way I have this working currently is hooking into an event that is fired in the first control through code-behind. I am thinking that making one or both of the properties DependencyProperties which would eliminate the need for the code-behind.
public partial class UserControl1 : UserControl
{
private DataModel1 dm;
public UserControl1()
{
this.DataContext = new DataModel1();
dm = (DataModel1)DataContext;
InitializeComponent();
}
public DataValue CurrentValue
{
get { return dm.CurrentValue; }
set { dm.CurrentValue = value; }
}
}
public class DataModel1 : INotifyPropertyChanged
{
private DataValue _myData = new DataValue();
public DataValue CurrentValue
{
get { return _myData; }
set { if (_myData != value) {_myData = value OnPropertyChanged("CurrentValue"); }
}
// INotifyPropertyChanged Section....
}
The property is just a pass through from the DataModel1 class.
Both UserControls are very similar in their structure and have the same public properties. I would like to replace the code behind eventhandler with a Binding similar, I think to:
<my:UserControl1 Name="UserControl1" />
<my:UserControl2 CurrentValue={Binding ElementName="UserControl1", Path="CurrentValue"} />
but the standard examples of DependencyProperties have getters and setter that use the GetValue and SetValue functions which use a generated backing object instead of allowing a pass through.
public DataValue CurrentValue
{
get { return (DataValue)GetValue(CurrentValueProperty); }
set { SetValue(CurrentValueProperty, value); }
}
I think the DP should look like:
public static readonly DependencyProperty CurrentValueProperty =
DependencyProperty.Register("CurrentValue", typeof(DataValue), typeof(UserControl1));
How can I change the definition of the public backing property to support the databinding pass through?
I found that jumping into the OnPropertyChanged event allowed me to pass the data through to the DataModel1. I am not 100% sure that this is the correct answer but it gets the job done.
Here is the corrected code:
public static readonly DependencyProperty CurrentValueProperty =
DependencyProperty.Register("CurrentValue", typeof(DataValue), typeof(UserControl1),
new PropertyMetadata(new PropertyChangedCallback(OnCurrenValueChanged)));
private static void OnCurrentValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UserControl1 uc = d as UserControl1;
if (e.NewValue != null)
{
uc.dm.CurrentValue = e.NewValue as DataValue;
}
}
public DataValue CurrentValue
{
get { return GetValue(CurrentValueProperty) as DataValue; }
set { SetValue(CurrentValueProperty, value); }
}

How to bind a dependency property for a custom control to its view models property

I am trying to bind a custom control's dependency property to its ViewModel's property.
The custom control looks like:
public partial class MyCustomControl : Canvas
{
//Dependency Property
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(MyCustomControl));
private VisualCollection controls;
private TextBox textBox;
public string Text
{
get { return textBox.Text; }
set
{
SetValue(TextProperty, value);
textBox.Text = value;
}
}
//Constructor
public MyCustomControl ()
{
controls = new VisualCollection(this);
InitializeComponent();
textBox = new TextBox();
textBox.ToolTip = "Start typing a value.";
controls.Add(textBox);
//Bind the property
this.SetBinding(TextProperty, new Binding("Text") {Mode = BindingMode.TwoWay, Source = DataContext});
}
}
And the View Model looks like:
-------
public class MyCustomControlViewModel: ObservableObject
{
private string _text;
public string Text
{
get { return _text; }
set { _text = value; RaisePropertyChanged("Text");}
}
}
----------
This binding for "Text" Property is not working for some reason.
What I am trying to do is that in the actual implementation I want the text property of MyCustom Control to update when I update the Text property of my underlying ViewModel.
Any help regarding this is much appreciated.
After some research I finally figured out the problem with my code. I got this code working by creating a Static event handler that actually sets the new property value to the underlying public member of the dependency property. The Dependency property declaration is like :
//Dependency Property
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(MyCustomControl), new PropertyMetadata(null, OnTextChanged));
and then define the static method that sets the property is like :
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyCustomControl myCustomControl = (MyCustomControl)d;
myCustomControl.Text = (string) e.NewValue;
}
this is the only thing I was missing.
Cheers
Just bind to the dependency property
<MyCustomControl Text="{Binding Path=Text}" />
You should bind your member TextBox to your TextProperty instead. I am pretty sure that the binding in xaml on your Text property overrides the one you make in the constructor.

How Set And Get data using the same Dependency Property?

I am working on a WPF project. And I just created a dependency property. This dependency property is intended to make the RichTextBox.Selection.Text property bindeable.
But what I cannot do is get and set data to RichTextBox.Selection.Text using the same DP.
This is the code that I used if I ONLY want to get data from my RichTextBox.Selection.Text using binding:
public class MyRichTextBox: RichTextBox
{
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(MyRichTextBox),
new PropertyMetadata(
TextPropertyCallback));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public MyRichTextBox()
{
this.TextChanged += new TextChangedEventHandler(MyRichTextBox_TextChanged);
}
void MyRichTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
Text = this.Selection.Text;
}
And it works perfectly, but with this code I cannot send any data from my ViewModel class.
So, if I ONLY want to set data to the RichTextBox.Selection.Text property from my ViewModel I used this code:
public class MyRichTextBox: RichTextBox
{
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(MyRichTextBox),
new PropertyMetadata(
TextPropertyCallback));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
private static void TextPropertyCallback(DependencyObject controlInstance, DependencyPropertyChangedEventArgs args)
{
MyRichTextBox instance = (MyRichTextBox)controlInstance;
instance.Selection.Text = (String)args.NewValue;
}
So, What Can I do if I want to be able to get and set data using the same dependency property???
Hope someone can help me, tahnk you in advance
Instead of binding your input control's text to the VM's property, you have binded an attached property to it, and in that attached property's value changed you set the input control's text.
In other words - there is nothing monitoring the changed to the input control's text.
Edit:
You are still doing:
instance.Selection.Text = (String)args.NewValue;
However, there is no notification of change to instance.Selection.Text.

Categories

Resources