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?
Related
When I set the value of IsClosed during runtime, OnIsClosedChanged() is called fine.
However, the Designer sets the value of the property but does not call the OnIsClosedChanged().
public static DependencyProperty IsClosedProperty = DependencyProperty.Register("IsClosed", typeof(bool), typeof(GroupBox), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender));
public bool IsClosed {
get {
return (bool)this.GetValue(IsClosedProperty);
}
set {
if ((bool)this.GetValue(IsClosedProperty) == value)
return;
this.SetValue(IsClosedProperty, value);
OnIsClosedChanged();
}
}
private void OnIsClosedChanged() {
_rowDefContent.Height = new GridLength((IsClosed ? 0 : 1), GridUnitType.Star);
}
Obviously IsClosed is not modified by the Designer and only IsClosedProperty receives the xaml change.
My question is: How can I run IsClosed after the value has been modified in the Designer. Or at least add some logic to the non-runtime changes.
You would have to register a PropertyChangedCallback with property metadata.
The reason is that dependency properties set in XAML or by bindings or some other source do not invoke the CLR wrapper (the setter method). The reason is explained in the XAML Loading and Dependency Properties article on MSDN:
For implementation reasons, it is computationally less expensive to
identify a property as a dependency property and access the property
system SetValue method to set it, rather than using the property
wrapper and its setter.
...
Because the current WPF implementation of the XAML processor behavior
for property setting bypasses the wrappers entirely, you should not
put any additional logic into the set definitions of the wrapper for
your custom dependency property. If you put such logic in the set
definition, then the logic will not be executed when the property is
set in XAML rather than in code.
Your code should look like this:
public static readonly DependencyProperty IsClosedProperty =
DependencyProperty.Register(
"IsClosed", typeof(bool), typeof(GroupBox),
new FrameworkPropertyMetadata(false,
FrameworkPropertyMetadataOptions.AffectsRender,
(o, e) => ((GroupBox)o).OnIsClosedChanged()));
public bool IsClosed
{
get { return (bool)GetValue(IsClosedProperty); }
set { SetValue(IsClosedProperty, value); }
}
private void OnIsClosedChanged()
{
_rowDefContent.Height = new GridLength((IsClosed ? 0 : 1), GridUnitType.Star);
}
Found the answer myself now. ValidateValueCallback comes really close! (as Alex K has pointed out) But it is a static method and I don't get any reference to the instance which has been changed. The key is to use a PropertyChangedCallback in FrameworkPropertyMetadata which is also an argument passed to the Property.Register method.
See:
public static DependencyProperty IsClosedProperty = DependencyProperty.Register("IsClosed", typeof(bool), typeof(GroupBox), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(OnIsClosedChangedPCC)));
public bool IsClosed {
get {
return (bool)this.GetValue(IsClosedProperty);
}
set {
this.SetValue(IsClosedProperty, value);
OnIsClosedChanged();
}
}
private static void OnIsClosedChangedPCC(DependencyObject d, DependencyPropertyChangedEventArgs e) {
GroupBox current = (GroupBox)d;
current.IsClosed = current.IsClosed;
}
private void OnIsClosedChanged() {
_rowDefContent.Height = new GridLength((IsClosed ? 0 : 1), GridUnitType.Star);
}
That does now re-set the IsClosedValue which triggers the OnIsClosedChanged to run.
Thank's for your help guys!
I have some C# code of the following form:
class FooBehavior : Behavior<Foo>
{
public static readonly DependencyProperty FooPropProperty = DependencyProperty.Register("FooProp", typeof(string), typeof(FooBehavior),
new FrameworkPropertyMetadata(FooPropProperty_Changed)
{
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
});
public String FooProp { get { return (string)GetValue(FooPropProperty); } set { SetValue(FooPropProperty, value); } }
private static void FooPropProperty_Changed(object sender, DependencyPropertyChangedEventArgs args)
{
/* re-entrancy check done here*/
// ...
if (/* args.NewValue is something bad that I can't use */)
FooProp = (string)args.OldValue;
try
{
/* do stuff here that might throw */
}
catch
{
FooProp = /* some known good value */;
throw;
}
}
}
I've taken out most of the actual code, but this shows the basics. What is happening is that I have a DependencyProperty where I want to to do validation on the property change, but I can't do it via the normal ValidateValueCallback method. I'd like to set the DependencyProperty back to its original value or to a known good value and have that change propagate to the viewmodel property to which the DependencyProperty is bound.
The problem I have is that when I get a value that I can't use, I can change the DependencyProperty's value, but the viewmodel doesn't see this new value. How can I get the value to propagate back to the viewmodel?
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.
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.
I made own dependency property like this
public bool Visible
{
get
{
return (bool)GetValue(VisibleProperty);
}
set
{
System.Windows.Visibility v = value == true ? System.Windows.Visibility.Visible : System.Windows.Visibility.Hidden;
SetValue(VisibleProperty, value);
border.Visibility = v;
}
}
public static readonly DependencyProperty VisibleProperty = DependencyProperty.Register("Visible", typeof(bool), typeof(SpecialMenuItem), new UIPropertyMetadata(false));
I'm binding my app property to it, but no luck.
App property(app.xaml.cs) and binding:
public bool IsGamePlaying
{
get
{
return isGamePlaying;
}
set
{
isGamePlaying = value;
}
}
private bool isGamePlaying;
<my:SpecialMenuItem ... Visible="{Binding Path=IsGamePlaying, Source={x:Static Application.Current}}" />
When I'm in debug, it reads the IsGamePlaying property, but doesn't make attempt to set the Visible property
How make it work?
You cannot include any logic in your dependency property wrapper. WPF will, where possible, directly call GetValue/SetValue rather than use your CLR property. You should include any extraneous logic by using metadata in your dependency property. For example:
public static readonly DependencyProperty VisibleProperty = DependencyProperty.Register("Visible", typeof(bool), typeof(SpecialMenuItem), new FrameworkPropertyMetadata(false, OnVisibleChanged));
private static void OnVisibleChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
// logic here will be called whenever the Visible property changes
}
The CLR wrapper is merely a convenience for consumers of your class.