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;
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 a ViewModel like this
Public class AboutPageViewModel
{
public AboutPageViewModel()
{
AppName = Settings.MyAppName;
}
private string _appName;
public string AppName
{
get{return _appName;}
set{_appName = value; RaisePropertyChanged("AppName");}
}
}
Now in a static class
public static class Settings
{
public static string MyAppName{get;set;} = "LOL"
}
How do I notify the ViewModel everytime MyAppName is changed, and update it to the Binded UI?
Thanks!
As you define it in your question, Settings isn't a static class (ah, I see in comments that was a typo, and it's static in your code). It should not be static. PropertyChanged notifications on a static class are theoretically possible but it's not worth your time to mess with, and there's no need to bother.
Have Settings implement INotifyPropertyChanged, just like your viewmodel. When MyAppName changes, Settings should raise PropertyChanged, just as AboutPageViewModel does when its own AppName property changes.
Now give Settings a static property called Instance:
public static Settings Instance { get; private set; }
static Settings()
{
Instance = new Settings();
}
And handle its PropertyChanged event in AboutPageViewModel:
public AboutPageViewModel()
{
AppName = Settings.Instance.MyAppName;
Settings.Instance.PropertyChanged += (s,e) =>
{
// If you're in C#6:
//if (e.PropertyName == nameof(Settings.MyAppName))
if (e.PropertyName == "MyAppName")
{
AppName = Settings.Instance.MyAppName;
}
}
}
Option Number Two
Arguably a better option; I've done it this way more than once.
In comments, #MikeEason makes the very good point that this could also be done with a custom *Changed event such as MyAppNameChanged, which has two advantages: It lets you go back to a static class, and it lets you skip the check on the property name, which is extra code and also a "magic string". Working with INotifyPropertyChanged we get a little bit numb to the danger of magic strings, but they are in fact bad. If you're in C#6, you can and absolutely should use the nameof() operator, but not all of us are in C#6 just yet. My main responsibility at work is an application that we're hoping to migrate to C#6 this summer.
public static event EventHandler<String> MyAppNameChanged;
private static String _myAppName = "";
public static String MyAppName {
get { return _myAppName; }
set {
if (_myAppName != value)
{
_myAppName = value;
// C#6 again. Note (thanks OP!) you can't pass this for sender
// in a static property.
MyAppNameChanged?.Invoke(null, value);
}
}
}
The drawback of this is that, well, this class is called Settings, not Setting. Maybe it's got a dozen properties changing here and there. That gets to be a real thicket of distinct property-changed events ("so what?" you may ask -- and you may have a point). My tendency is to stick with PropertyChanged if there's a whole sheaf of them, and to add an event if the class has only one or two important properties that somebody needs to keep an eye on. Either way is annoying in my view; try both and you'll eventually settle on a preference.
You don't need to store value in ViewModel if you already have it somewhere (I assume what you are not going to change it in ViewModel itself):
public class AboutPageViewModel : INotifyPropertyChanged
{
public string AppName => Settings.MyAppName;
}
And as for View to know when this property is changed you need 2 things: 1) there should be a way to inform ViewModel when value is changed 2) rise PropertyChanged(nameof(AppName)) (notice INotifyPropertyChanged).
Several possibilities to make it:
Settings should rise event when MyAppName value is changed, ViewModel subscribe to it and rises PropertyChanged;
Store initial value, check periodically if value is changed;
Use another type which implement INotifyPropertyChanged, bind to that type property instead, this will update view automatically if that type rises PropertyChanged.
You have to implement INotifyPropertyChanged interface on Settings class!
then use the same piece of code like this:
private string _myAppName;
public string MyAppName
{
get{return _myAppName;}
set{_appName = value; RaisePropertyChanged("MyAppName");}
}
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 want two share a DepedencyProperty between to classes using AddOwner (any other approach is welcome), e.g.
class ClassA : DependencyObject
{
public int Number
{
get { return (int)GetValue(NumberProperty); }
set { SetValue(NumberProperty, value); }
}
public static readonly DependencyProperty NumberProperty =
DependencyProperty.Register("Number", typeof(int), typeof(ClassA),
new FrameworkPropertyMetadata(0,
FrameworkPropertyMetadataOptions.Inherits));
}
and
class ClassB : DependencyObject
{
public int Number
{
get { return (int)GetValue(NumberProperty); }
set { SetValue(NumberProperty, value); }
}
public static readonly DependencyProperty NumberProperty =
ClassA.NumberProperty.AddOwner(typeof(ClassB),
new FrameworkPropertyMetadata(0,
FrameworkPropertyMetadataOptions.Inherits));
}
like described here. As you might guess: Of course it doesn't work. That makes perfect sense, because it would make it impossible to create multiple instances of the same class that all have their "own" dependency property.
How do I make sure that all classes (and especially all instances) of ClassA, ClassB and any other class which refers to the property are talking about the exact same property (and therefore value)? A Singleton is no option, since Class A is a MainWindow and Class B is an UserControl (protected constructors are therefore not possible).
Regards,
Ben
I think you're misunderstanding the purpose of DependencyProperties.
They are basically a Property Definition, without a property Value.
They define things like name, type, default value, location of the value, etc however they do not contain the actual value itself. This allows the value to be provided with a binding pointing to any other property in any other location.
Your best bet is to probably just create a property that is backed by a singleton property.
public int Number
{
get { return MySingleton.Number; }
set { MySingleton.Number = value; }
}
Edit
Based on comments below where you say you want all instances of the object to respond to change notifications from any of the other objects, you'd want to implement INotifyPropertyChanged on your singleton object, and subscribe to it's PropertyChange event in each class that uses that value.
For example,
public ClassA
{
public ClassA()
{
MySingleton.PropertyChanged += Singleton_PropertyChanged;
}
void Singleton_PropertyChanged(object sender, NotifyPropertyChangedEventArgs e)
{
// if singleton's Number property changed, raise change
// notification for this class's Number property too
if (e.PropertyName == "Number")
OnPropertyChanged("Number");
}
public int Number
{
get { return MySingleton.Number; }
set { MySingleton.Number = value; }
}
}
One possible solution to what you want here is to use another class where you store that
value. e.g.
public class SomeValueStore : IValueStore
{
int myValue {get; set;}
}
Then, whereever you need that value, you can use Dependency injection to get it.
somewhere at Bootstrapper:
RootContainer.Register<IValueStore>(new SomeValueStore);
and in code:
var valueStore = RootContainer.Resolve<IValueStore();
valueStore.myValue = 42;
This is just an idea (And I know we have a ServiceLocator here).
Perhaps you can store a reference to that ValueStore somewhere where you
can get it from both classes you need it as a simple solution.
public SomeClassYouHaveAccessToFromBothSides
{
public IValueStore _store = new SomeValueStore();
}
Please excuse me. I do not have access to my repo / visual studio right now
so I cannot give better example. But I think the underlying idea is clear.
I've got a UserControl class called A and that one contains a Border Property. Then others classes are inherited from A class, but I cannot use my new Property.
public class A : UserControl
{
public A()
{
Border2 = new Border();
Border2.BorderBrush = Media.Brushes.LightGray;
}
public static readonly DependendyProperty Border2Property = DependencyProperty.Register("Border2", typeof(Border), typeof(A));
public Border Border2
{
get { return (Border)GetValue(Border2Property); }
set { SetValue(Border2Property, value); }
}
}
Then when I use another class where is inherited from A, I cannot use this Border2 Property, I'm writing something like:
<local:A.Border2></...
But it tells me that Border2 property doesn't support values of type Grid.
That's because you've created a standard dependency property. If you want to be able to set it on other types besides A, then you want to create an attached property instead. This only takes a handful of code changes:
Register it by calling DependencyProperty.RegisterAttached (instead of .Register)
Add static GetBorder2 and SetBorder2 methods to class A. Even if your code doesn't call these methods, they're part of the pattern and need to be there -- they're how you tell the compiler that yes, you do intend for people to be able to set this attached property in XAML.
For example:
public static readonly DependencyProperty Border2Property =
DependencyProperty.RegisterAttached("Border2", typeof(Border), typeof(A));
public static Border GetBorder2(DependencyObject obj)
{
return (Border) obj.GetValue(Border2Property);
}
public static void SetBorder2(DependencyObject obj, Border2 value)
{
obj.SetValue(Border2Property, value);
}
If your property should only be available for certain element types -- e.g. if it should only apply to FrameworkElement and its descendants, or to Panel and its descendants, or something like that -- then use that as the type of the first parameter to GetBorder2 and SetBorder2.