I want to create something like late binding for a DependencyProperty.
I have a ListView with icons. I want icons loaded only when they are actually needed / displayed. When the icon elements are displayed, GetValue on IconProperty is called, but it only can return default value (which is null). I want to inject code to load relevant icon when initial value is null.
My first approach was creating custom getter / setter for the property without using DependencyProperty at all. It works, however I wonder if it's optimal.
When I use DependencyProperty I can easily determine when it's changed via OnPropertyChanged override. I have no idea when should I inject initialization for getter.
public class DisplayItem : DependencyObject {
// ...
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register(
"Icon",
typeof(ImageSource),
typeof(DisplayItem),
null
);
public ImageSource Icon {
get { return (ImageSource)GetValue(IconProperty); }
private set { SetValue(IconProperty, value); }
}
private void GetIcon() {
// Some code to actually fetch the icon image...
// ...
Icon = loadedImageSource;
}
// ...
}
Consider the code above: how to call GetIcon() exactly before first GetValue() occurs?
Do not use a dependency property.
A plain CLR property (with an optional INotifyPropertyChanged implementation) is sufficient:
public class DisplayItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ImageSource icon;
public ImageSource Icon
{
get
{
if (icon == null)
{
icon = ... // load here
}
return icon;
}
private set
{
icon = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Icon"));
}
}
}
Why do you want to know when the dependency property value is being accessed?
If bound to some property in the view, the property will be accessed on initially loading the components. Hence, you can call that GetIcon() when on Loaded. If you’re using MVVM, just bind the Loaded event to some command, else simply handle the event and call the function.
If you’re planning to move to MVVM pattern, just using a CLR property as the other answer suggests, would do the trick.
Related
I'd first like to say I'm very new to Binding.. I've done some things in WPF already but I never used binding because concept is a bit too hard to understand for me right of the bat. Even this what I'm doing now is something i managed to salvage from a tutorial that I didn't fully understand.
In my application I have a static class with static properties and there's a static method that changes those static properties.
Example:
public static class AppStyle
{
public static SolidColorBrush property = Brushes.Red;
public static void ChangeTheme()
{
property = Brushes.Blue;
}
}
Inside the XAML I have a control that has it's background binded to this value. I even declared the namespace properly.
...
xmlns:style="clr-namespace:CorrectNamespace;assembly=RightAssembly"
...
<TextBox x:Name="TXT_PN"
Background="{Binding Source={x:Static style:AppStyle.property}}"
TextChanged="TXT_PN_TextChanged"
Text="Text"/>
When the application loads it will load the correct setting (Red color) however when things change and ChangeTheme() is called, the static class will get the new value, however the textbox's Background will not change.
What am I doing wrong here? As I said, I'm very new to this and I would appreciate the solution in laymen's terms.
Thank you!
First of all, your property is actually not a property, but a field. A minimal property declaration would look like this:
public static SolidColorBrush Property { get; set; }
Please note the name is starting with an uppercase letter, which is a widely accepted coding convention in C#.
Because you also want to have a change notification fired whenever the value of the property changes, you need to declare a property-changed event (which for non-static properties is usually done by implementing the INotifyPropertyChanged interface).
For static properties there is a new mechanism in WPF 4.5 (or 4.0?), where you can write a static property changed event and property declaration like this:
public static class AppStyle
{
public static event PropertyChangedEventHandler StaticPropertyChanged;
private static void OnStaticPropertyChanged(string propertyName)
{
StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
}
private static SolidColorBrush property = Brushes.Red; // backing field
public static SolidColorBrush Property
{
get { return property; }
set
{
property = value;
OnStaticPropertyChanged("Property");
}
}
public static void ChangeTheme()
{
Property = Brushes.Blue;
}
}
The binding to a static property would be written with the property path in parentheses:
Background="{Binding Path=(style:AppStyle.Property)}"
To implement reaction on a change, you need to notify about the change. See INotifyPropertyChanged interface. However, you can't use it with a static class. What about a singleton (ideally using some dependency injection container) instead of a static class?
I have in my C# WPF solution as follows:
Mainwindow with a startupControl (always running)
Dialogwindow with diffent other controls.
A public Helper-class containing some public static properties to indicate what department at customer is active, and for who i have focus on at the moment.
I want simply two XAML textBlocks displayed in my Startupcontrol to show the property names if and when the value for a department or costumer has been set.
I think it could properbly work smooth with some sort of binding, but i dont know anything about bindings, other than they exists.
Is it possible in any way from my controls in my dialogwindow, to change the value of the 2 textblocks in the Startupcontrol ?
As the program is small and I know exactly when the values change, I think i could make a function setting the value ex.:
activeDepartmentTextBlock.Text = HelperClass.ActiveDepartment.Name;
But from my control.cs in the DialogWindow, it seems to be possible to reach the activeDepartmentTextBlock.
Anyone who can help me ?
Since WPF 4.5, binding to static properties with property change notification is quite simple.
The example below assumes that you want to notify about the change of the ActiveDepartment property of the HelperClass (and not about the Name property of the Department object). In addition to the static property, declare a static event named StaticPropertyChanged and fire it when the static property changes:
public class Department
{
public string Name { get; set; }
}
public class HelperClass
{
public static event PropertyChangedEventHandler StaticPropertyChanged;
private static Department activeDepartment;
public static Department ActiveDepartment
{
get => activeDepartment;
set
{
activeDepartment = value;
StaticPropertyChanged?.Invoke(null,
new PropertyChangedEventArgs(nameof(ActiveDepartment)));
}
}
}
You can bind to a static property like this:
<TextBlock Text="{Binding Path=(local:HelperClass.ActiveDepartment).Name}"/>
Binding is a good solution but you have static property so you can't use binding infrastructure directly to get notified of updates since there's no DependencyObject (or object instance that implement INotifyPropertyChanged) involved.
If the value does change and you need to update TextBlock's value in main window yo can create a singleton instead of static class to contain the value and bind to that.
An example of the singleton:
public class HelperClass : DependencyObject {
public static readonly DependencyProperty ActiveDepartmentProperty =
DependencyProperty.Register( "ActiveDepartment", typeof( Department ),
typeof( HelperClass ), new UIPropertyMetadata( "" ) );
public Department ActiveDepartment {
get { return (Department) GetValue( ActiveDepartmentProperty ); }
set { SetValue( ActiveDepartmentProperty, value ); }
}
public static HelperClass Instance { get; private set; }
static HelperClass() {
Instance = new HelperClass();
}
}
So binding will work like in an example below:
<TextBox Text="{Binding Source={x:Static local:HelperClass.Instance}, Path=ActiveDepartment.Name}"/>
It might look like a hard way and that’s it. You can use events model instead and add the event to your HelperClass. MainWindow can add event handler and change activeDepartmentTextBlock value when event raised.
public MainWindow()
{
InitializeComponent();
HelperClass.Instance.DepartmentChanged += OnDepartmentChanged;
}
private void OnDepartmentChanged(Department newDepartment)
{
activeDepartmentTextBlock.Text = newDepartment.Name;
}
Update. If you want to have the simplest solution you can break encapsulation principle and pass MainWindow as a parameter to DialogWindow and make activeDepartmentTextBlock public. So you will be able to save the link to the MainWindow in the DialogWindow's field and just change the text when you need in DialogWindow:
this.mainWindow.activeDepartmentTextBlock.Text = HelperClass.ActiveDepartment.Name;
I'd first like to say I'm very new to Binding.. I've done some things in WPF already but I never used binding because concept is a bit too hard to understand for me right of the bat. Even this what I'm doing now is something i managed to salvage from a tutorial that I didn't fully understand.
In my application I have a static class with static properties and there's a static method that changes those static properties.
Example:
public static class AppStyle
{
public static SolidColorBrush property = Brushes.Red;
public static void ChangeTheme()
{
property = Brushes.Blue;
}
}
Inside the XAML I have a control that has it's background binded to this value. I even declared the namespace properly.
...
xmlns:style="clr-namespace:CorrectNamespace;assembly=RightAssembly"
...
<TextBox x:Name="TXT_PN"
Background="{Binding Source={x:Static style:AppStyle.property}}"
TextChanged="TXT_PN_TextChanged"
Text="Text"/>
When the application loads it will load the correct setting (Red color) however when things change and ChangeTheme() is called, the static class will get the new value, however the textbox's Background will not change.
What am I doing wrong here? As I said, I'm very new to this and I would appreciate the solution in laymen's terms.
Thank you!
First of all, your property is actually not a property, but a field. A minimal property declaration would look like this:
public static SolidColorBrush Property { get; set; }
Please note the name is starting with an uppercase letter, which is a widely accepted coding convention in C#.
Because you also want to have a change notification fired whenever the value of the property changes, you need to declare a property-changed event (which for non-static properties is usually done by implementing the INotifyPropertyChanged interface).
For static properties there is a new mechanism in WPF 4.5 (or 4.0?), where you can write a static property changed event and property declaration like this:
public static class AppStyle
{
public static event PropertyChangedEventHandler StaticPropertyChanged;
private static void OnStaticPropertyChanged(string propertyName)
{
StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
}
private static SolidColorBrush property = Brushes.Red; // backing field
public static SolidColorBrush Property
{
get { return property; }
set
{
property = value;
OnStaticPropertyChanged("Property");
}
}
public static void ChangeTheme()
{
Property = Brushes.Blue;
}
}
The binding to a static property would be written with the property path in parentheses:
Background="{Binding Path=(style:AppStyle.Property)}"
To implement reaction on a change, you need to notify about the change. See INotifyPropertyChanged interface. However, you can't use it with a static class. What about a singleton (ideally using some dependency injection container) instead of a static class?
I have a UserControl with a Dependency Property:
public static readonly DependencyProperty Step2CommandProperty =
DependencyProperty.Register("Step2Command", typeof(ICommand), typeof(MyTripNavigationStep), new PropertyMetadata(null));
public ICommand Step3Command
{
get { return (ICommand)GetValue(Step3CommandProperty); }
set { SetValue(Step3CommandProperty, value); }
}
Then I have a ViewModel with a ICommand property:
public ICommand SaveStep1Command
{
get
{
return new RelayCommand(() =>
{
});
}
}
Then I Bind the two properties like this in the Page where i have the viewModel as the DataContext and the UserControl.
<UserControls:Step Step3Command="{Binding SaveStep1Command, Mode=OneWay}" />
The binding is not being applied, and the Step3Command in the userControl always appears to be null.
I know that the DataContext is working fine, and that Visual Studio does not allow me to put TwoWay binding. I'm using GalaSoft Simple Mvvm and Visual Studio CTP update 2.
Anybody has a clue of what am I doing wrong? thanks.
You defined the property wrong. The get block gets called everytime the property is accessed, so everytime you (or the MVVM magic in WPF) accesses SaveStep1Command a new command is created. This is not what you want.
Rewrite the property like this:
In your constructor code, write:
SaveStep1Command = new RelayCommand(...)
And define your property just like this:
public ICommand SaveStep1Command { get; }
If you are on an older version of .net / C#, you must define it like this:
public ICommand SaveStep1Command { get; private set; }
Explanation attempt: It might be that the databinding only creates weak references. With your way of defining the SaveStep1Command it is created once the binding is set up, and then it just "lies around" on the heap - and when the GC kicks in next time, the space is freed up because it has no strong references.
Yes. The problem is that you are not handling the Changed event of the DP.
Reference this: https://blog.jerrynixon.com/2013/07/solved-two-way-binding-inside-user.html
I'm trying to use the MVVM pattern in my Silverlight 3 application and am having problems getting binding to a command property of a view model working. First off, I'm trying to add an attached property called ClickCommand, like this:
public static class Command
{
public static readonly DependencyProperty ClickCommandProperty =
DependencyProperty.RegisterAttached(
"ClickCommand", typeof(Command<RoutedEventHandler>),
typeof(Command), null);
public static Command<RoutedEventHandler> GetClickCommand(
DependencyObject target)
{
return target.GetValue(ClickCommandProperty)
as Command<RoutedEventHandler>;
}
public static void SetClickCommand(
DependencyObject target, Command<RoutedEventHandler> value)
{
// Breakpoints here are never reached
var btn = target as ButtonBase;
if (btn != null)
{
var oldValue = GetClickCommand(target);
btn.Click -= oldValue.Action;
target.SetValue(ClickCommandProperty, value);
btn.Click += value.Action;
}
}
}
The generic Command class is a wrapper around a delegate. I'm only wrapping a delegate because I wondered if having a delegate type for a property was the reason things weren't working for me originally. Here's that class:
public class Command<T> /* I'm not allowed to constrain T to a delegate type */
{
public Command(T action)
{
this.Action = action;
}
public T Action { get; set; }
}
Here's how I am using the attached property:
<Button u:Command.ClickCommand="{Binding DoThatThing}" Content="New"/>
The syntax seems to be accepted, and I think that when I tested all of this with a string property type, that worked fine. Here's the view model class that is being bound to:
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public Command<RoutedEventHandler> DoThatThing
{
get
{
return new Command<RoutedEventHandler>(
(s, e) => Debug.WriteLine("Never output!"));
}
}
}
The delegate contained in the Command property is never invoked. Also, when I place breakpoints in the getter and setter of the attached property, they are never reached.
In trying to isolate the problem, I changing the property type to string; the breakpoint in the getter and setter was also never reached, yet throwing an exception in them did cause the application to terminate, so I am thinking it's a framework eccentricity.
Why is this stuff not working? I also welcome alternate, hopefully simpler ways to bind event handlers to view models.
You have at least two problems here.
First, you are relying on the SetXxx method being executed. The CLR wrappers for dependency properties (the property setter or SetXxx method) are not executed when the DP is set from XAML; rather, WPF sets the value of the internally managed DP "slot" directly. (This also explains why your breakpoints were never hit.) Therefore, your logic for handling changes must always occur in the OnXxxChanged callback, not in the setter; WPF will call that callback for you when the property changes regardless of where that change comes from. Thus (example taken from a slightly different implementation of commands, but should give you the idea):
// Note callback in PropertyMetadata
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(Click),
new PropertyMetadata(OnCommandChanged));
// GetXxx and SetXxx wrappers contain boilerplate only
public static ICommand GetCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(CommandProperty);
}
public static void SetCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(CommandProperty, value);
}
// WPF will call the following when the property is set, even when it's set in XAML
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ButtonBase button = d as ButtonBase;
if (button != null)
{
// do something with button.Click here
}
}
Second, even with this change, setting ClickCommand on a control that doesn't already have a value set will cause an exception, because oldValue is null and therefore oldValue.Action causes a NullReferenceException. You need to check for this case (you should also check for newValue == null though this is unlikely ever to happen).