C# Binding global static string to UWP Textblock - c#

everybody.
Does anyone knows how to bind global static string to UWP Textblock, with propertychange update in control?
I have tried a lot of things, such as:
Text="{Binding Path={local:AppSettings.StorageFolder}, Mode=OneWay}"
Text="{x:Bind Path=(local:AppSettings.StorageFolder), RelativeSource={RelativeSource Self}, Mode=OneWay}"
And none works. Always some error comes up, like:
"Nested types are not supported and
Value does not fall within the expected range"
I have managed to bind it to non static value in my class:
Text="{x:Bind viewModel.MyValue, Mode=OneWay}"
Any solution to this?
Thanx.

You simply can't bind to a static property in UWP the same way as you can in WPF. There is no x:Static markup extension available.
You have some options:
If the DataContext of the element is an instance of the type in which the static property is defined, you could bind to a static property as usual:
<TextBlock Text="{Binding MyStaticProperty}" />
public sealed partial class BlankPage1 : Page
{
public BlankPage1()
{
this.InitializeComponent();
this.DataContext = this;
}
public static string MyStaticProperty { get { return "Static..."; } }
}
If the static property is defined in another class, your best option would be to wrap the static property in a non-static one:
public sealed partial class BlankPage1 : Page
{
public BlankPage1()
{
this.InitializeComponent();
this.DataContext = this;
}
public static string MyWrapperProperty { get { return MyStaticClass.MyStaticProperty; } }
}
public static class MyStaticClass
{
public static string MyStaticProperty { get { return "Static..."; } }
}
If you want property change notifications it makes no sense to bind to a static property at all because the source object of a property must implement the INotifyPropertyChanged interface for you to be able to refresh the target property dynamically by raising the PropertyChanged event.
You could still wrap the static property in a non-static one of a view model that implements the INotifyPropertyChanged interface:
public class ViewModel : INotifyPropertyChanged
{
public string MyNonStaticProperty
{
get { return MyStaticClass.MyStaticProperty; }
set { MyStaticClass.MyStaticProperty = value; NotifyPropertyChanged(); }
}
//...
}
public static class MyStaticClass
{
public static string MyStaticProperty { get; set; }
}
You will obviosuly need to call NotifyPropertyChanged("MyNonStaticProperty") from the view model class whenever you want to refresh the target property in the view.

Currently in UWP when using x:Bind the end property (I.e. at the end of the path) must be mutable / nonstatic. However you can reference static properties prior to that such as x:Bind local:MyClass.Singleton.NonstaticProperty.
The use of functions at the end of the x:Bind property path can also address this kind of challenge.

Related

Return singleton value in MVVM for binding

I am trying to figure out if it is possible to use a value from a singleton as my binding. I want to do something like this?
public class MySingleton : INotifyPropertyChanged
{
//...inotifypropertychanged and singleton implementation
private bool _isChecked;
public bool IsChecked
{
get
{
return _isChecked;
}
set
{
_isChecked= value;
OnPropertyChanged("IsChecked");
}
}
//...other implementation
}
public class MyViewModel : INotifyPropertyChanged
{
//...inotifypropertychanged and other implementation
public bool IsAllChecked { get { return MySingleton.GetInstance().IsChecked; } }
}
Some Xaml:
<ToggleButton IsChecked = "{Binding IsAllChecked}"/>
I have tried this and the bindings don't seem to update. I have tried this with ObservableCollection and it works great but other types aren't. I figure that it is something special with ObservableCollection.
MyViewModel doesn't raise PropertyChanged for IsAllChecked property, update in UI will not happen (ObservableCollection is totally different case - INotifyCollectionChanged).
why not declare Instance property instead of GetInstance() method and bind to MySingleton.Instance directly?
"{Binding Path=IsChecked, Source={x:Static myNameSpace:MySingleton.Instance}}"

Setting attached property in App.xaml

I have the following app.xaml:
<BaseApp x:Class ="MyApp"
xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local ="clr-namespace:MyApp"
Props.MyCustom="test"
Startup ="Application_Startup"
>
<Application.Resources >
< ResourceDictionary>
...
</ ResourceDictionary>
</Application.Resources>
I then need to be able to read the property from the base application:
public partial class BaseApp : Application
{
static void MyFunc()
{
// Access property from here
var myvar = Props.MyCustom
}
}
I'm currently working on the belief that this needs to be in a separate class, as follows:
public class Props : DependencyObject
{
public string MyCustom
{
get { return ( string)GetValue(MyCustomProperty); }
set { SetValue(MyCustomPropertyKey, value); }
}
public static readonly DependencyPropertyKey MyCustomPropertyKey =
DependencyProperty.RegisterAttachedReadOnly("MyCustom" , typeof (string ), typeof (Props), new UIPropertyMetadata (0));
public static readonly DependencyProperty MyCustomProperty = MyCustomPropertyKey.DependencyProperty;
}
Am I approaching this in the right way, and if so, what do I need to do to be able to access this from app.xaml?
EDIT:
For future travellers, the way I finally managed this was to simply declare an abstract read-only property in the base class and override it in code behind. Not as neat as I would have liked, but it works.
Attached properties should have static getter and setter methods, in your case:
public static string GetMyCustom(DependencyObject obj)
{
return (string)obj.GetValue(MyCustomProperty);
}
public static void SetMyCustom(DependencyObject obj, string value)
{
obj.SetValue(MyCustomProperty, value);
}
Having said that you would still have to have an instance of a DependencyObject-derived class to set the property on, which System.Windows.Application is not. In other words, you can't set an attached property on your MyApp object.
Instead of using an attached property, you might just add a plain CLR property to your BaseApp class:
public class BaseApp : Application
{
public string MyCustom { get; set; }
}
and use it like this:
<local:BaseApp x:Class="MyNamespace.MyApp" ...
MyCustom="Hello">
<Application.Resources>
</Application.Resources>
</local:BaseApp>

New Instance of Object Breaks Binding

I have a class called Date which overrides its ToString and returns it via a property:
public string DateString
{
get { return ToString(); }
}
My instance of Date is held in a container class and manipulated from there. In XAML, the overridden ToString is displayed like so:
<TextBlock Text="{Binding Container.Date.DateString, Source={StaticResource Locator}}" />
Locator is defined in App.xaml:
<data:Locator x:Key="Locator" />
The Locator class includes:
public class Locator
{
static Locator()
{
Container = new GameContainer();
}
public static GameContainer Container { get; set; }
}
GameContainer includes:
public class GameContainer
{
public GameContainer()
{
Date = new Date();
}
public Date Date { get; set; }
}
I have also tried this with IOC containers with no luck.
When the program is launched, everything behaves properly; a button click increments the date (and calls RaisePropertyChanged on DateString). However, if I create a new instance of the container or a new instance of Date, the UI no longer updates and I can't figure out why. It only seems to happen after user interaction has occurred; I can create a new instance during various stages of the program's initialization without any problems, but once a button click causes a new instance to be created, the string no longer updates.
I have tried everything I can think of and I can't seem to figure out what is causing this to happen. Any help would be appreciated.
Update: Currently my Dependency Property is as follows:
public static readonly DependencyProperty ContainerProperty =
DependencyProperty.Register("Container", typeof(GameContainer), typeof(Locator));
public static GameContainer Container
{
get { return (GameContainer)GetValue(ContainerProperty); }
set
{
SetValue(ContainerProperty, value);
}
}
I'm getting this error: An object reference is required for the non-static field, method, or property 'System.Windows.DependencyObject.GetValue(System.Windows.DependencyProperty)'
Looks like you don't have any property changed notifying mechanism, this is very important to make the Binding work properly. Try implementing the INotifyPropertyChanged or creating the corresponding DependencyProperty, I'll introduce to using DependencyProperty in this case:
public class GameContainer : DependencyObject {
public GameContainer() {
Date = new Date();
}
public static readonly DependencyProperty DateProperty =
DependencyProperty.Register("Date", typeof(Date), typeof(GameContainer));
public Date Date {
get { return (Date) GetValue(DateProperty);}
set {
SetValue(DateProperty, value);
}
}
}
NOTE: do the same for the property Container in the class Locator.
I'm really confused how Binding deals with static property. This is all I can think of as a should-try code:
public class Locator {
static Locator() {
Container = new GameContainer();
}
public static event EventHandler ContainerChanged;
static GameContainer container;
public static GameContainer Container {
get { return container;}
set {
if(container != value){
container = value;
var handler = ContainerChanged;
handler(null, EventArgs.Empty);
}
}
}
}

How to bind to an attached property of a subproperty of a static property

I have the following code
public static class StaticClass
{
public static Instance Inst { get; set; }
}
public class Instance
{
public Button Butt { get; set; }
}
I need to bind to Grid.Row property attached to Butt button. I tried this:
{Binding Source={x:Static local:StaticClass.Inst.Butt}, Path=(Grid.Row)}
But it doesn't work because Butt is not static property. Usually I use x:Static to bind to static property and write the rest in Path, but in this case Path contains attached property. I don't know how to do this.
You need this, because, as you said, Butt is not static:
{Binding Source={x:Static local:StaticClass.Inst}, Path=Butt.(Grid.Row)}
That is, make Butt part of your Path, not part of the Source.

Binding visibility to static property

I have a control that has a label on it, that I would like to hide or show based on a global menu item for all instances of my control. If I click the button to hide labels, I want to to hide all of them.
My xaml looks like this:
<TextBlock Name="_label" Visibility="{Binding LabelShown}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
in my code behind I have a property:
private static Visibility _labelShown;
public static Visibility LabelShown
{
get { return _labelShown; }
set { _labelShown = value; }
}
And I set DataContext = this;
When I change the static property, nothing happens. I assume this is because no controls are getting a property changed notification. I cannot implement INotifyPropertyChanged on it, because I cannot reference the non static property changed handler from my static property.
I feel like maybe this isn't the best way to do this, but I would really like to have one button (many levels above my actual control) drive the visibility for all instances.
CodeNaked's solution works, but it uses a Singleton which has downsides when doing unit-testing. I prefer to approach global access problems by just having one settings instance at the application root, i.e. the App-class.
e.g.
public partial class App : Application
{
private static Settings _settings = new Settings();
public static Settings Settings
{
get { return _settings; }
}
...
Where this property contains all the settings for the application. Binding then looks like this:
"{Binding Source={x:Static local:App.Settings}, Path=LabelsShown}"
Edit: If you are worried about dependencies you could also inject a reference to those settings in the constructor of any class where you need it, using its minimal interface.
e.g.
public class MyControl : ContentControl
{
public interface IMyControlSettings
{
public bool LabelsShown { get; set; }
}
private IMyControlSettings _settings;
public MyControl(IMyControlSettings settings)
{
_settings = settings;
DataContext = _settings; // For easy binding, in most cases you probably do not want to do that since it prevents DataContext inheritance.
}
}
public class Settings : Test.MyControl.IMyControlSettings, INotifyPropertyChanged
{
public bool LabelsShown { get; set; }
...
}
You can do something like this:
public class MySettings : INotifyPropertyChanged {
private MySettings() {
}
private Visibility _labelShown;
public Visibility LabelShown
{
get { return _labelShown; }
set {
_labelShown = value;
// Raise PropertyChanged event for LabelShown
}
}
private static MySettings _instance;
public static MySettings Instance
{
get {
if (_instance == null)
_instance = new MySettings();
return _instance;
}
}
}
Then bind to it like {Binding Path=LabelShown, Source={x:Static local:MySettings.Instance}}
The only thing you need to add is the local xmlns which would be like xmlns:local="clr-namespace:MyNamespace"
I found a kinda lame workaround:
public static Visibility LabelShown
{
get { return _labelShown; }
set
{
_labelShown = value;
if ( StaticEvent != null )
{
StaticEvent();
}
}
}
private static event Action StaticEvent;
public event PropertyChangedEventHandler PropertyChanged
{
add { StaticEvent += () => value(this, new PropertyChangedEventArgs("LabelShown")); }
remove { StaticEvent -= () => value(this, new PropertyChangedEventArgs("LabelShown")); }
}
It works, but I am a little worried about the remove handler actually being able to remove the anonymous method like that. Would that cause memory problems if many controls are disposed?
I tend to prefer CodeNaked's solution, but I wanted to offer this for discussion.

Categories

Resources