How to set a static class to Data Context? - c#

I have a solution with 4 projects.
In the UI project, I have this XAML:
<Controls:MetroWindow x:Class="QuaverUI.Inicio.Principal"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:behaviours="clr-namespace:MahApps.Metro.Behaviours;assembly=MahApps.Metro"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:QuaverUI.Inicio"
xmlns:common="clr-namespace:Common;assembly=Common"
mc:Ignorable="d"
BorderThickness="0"
GlowBrush="Black"
Title="Quaver" Height="700" Width="1250"
WindowStartupLocation="CenterScreen"Closing="MetroWindow_Closing">
<Window.DataContext>
<common:Config/>
</Window.DataContext>
</Controls:MetroWindow>
As you can see, in the XAML I use the class common:Config as DataContext, and it is a "normal" class, but I want to use the following one instead:
public static class Sesion
{
public static int idSesion;
public static string usuarioSesion;
public static string rolSesion;
public static string nombres;
public static string primerApellido;
public static string segundoApellido;
public static string GetNombreCompleto()
{
return string.Format("{0} {1} {2}",
nombres, primerApellido, segundoApellido);
}
}
It's a static class, and it seems that I can't bind it to the UI.
What can I do? What do I have to put in the Binding of the Labels?

Since you cannot create an instance of a static class, it makes no sense to set the DataContext to your Sesion class or type.
But you can bind each of the elements to the static properties of this type like this:
<TextBlock Text="{x:Static local:Sesion.rolSesion}" />
...where local is mapped against the CLR namespace of the Sesion class.

You have 2 solutions:
1) Keep Sesion static and implement StaticPropertyChanged like #elgonzo suggested;
2) Make Sesion a singleton. This is a better solution because you have more control on the Sesion lifetime. Also, you can use Dependency Injection and other useful patterns that work only with class instances (for example the DataContext property), not static classes.
Singleton code snippet:
public class Sesion
{
static Sesion()
{
Instance = new Sesion();
}
public static Sesion Instance { get; }
private Sesion() { }
public int IdSesion { get; set; }
//other properties...
}
And you can then bind the DataContext in the MetroWindow.xaml.cs:
public partial class MetroWindow : Window
{
public MetroWindow()
{
InitializeComponents();
DataContext = Sesion.Instance;
}
}
Note also that a Binding work only with source PROPERTIES, not public fields or methods or whatever. And, if you need to listen to the changes in the Sesion class, you'll need to make Sesion to implement INotifyPropertyChanged interface.

I'm knowing it's two years old stuff, but there is a third solution as follows:
a) create a singleton class, e.g.
public class DataManager
{
private static DataManager __instance = null;
private static readonly object _padlock = new object();
public static DataManager Instance
{
get
{
if (__instance == null)
{
lock (_padlock)
{
if (__instance == null)
{
__instance = new DataManager();
}
}
}
return __instance;
}
}
private DataManager()
{ }
// your stuff as needed ...
}
b) in xaml declare the namespace the DataManager is in, e.g.
xmlns:data="clr-namespace:MyWonderfulApp.Namespace"
c) in xaml declare the data context
<Window.DataContext>
<ObjectDataProvider ObjectInstance="{x:Static data:DataManager.Instance"/>
</Window.DataContext>

Related

WPF/C# Link Two Variables together

I currently have a simple WPF application, in the MainWindow I will have a variable (In this case the variable is a class that holds data). Then I have a User Control which has the same variable.
Currently, I'm passing the variable with the ref keyword and it works perfectly fine, however, is this save/good practice? Is there a better way of linking this two variables together?
I am aware of the existence of DependencyProperty, however, I could not get it to work.
MainWindow:
public partial class MainWindow : Window
{
private TestClassWithInfo m_SelectedInfo;
public MainWindow()
{
InitializeComponent();
m_SelectedInfo = new DrawingInformation();
TestGridUC mp = new TestGridUC(ref m_SelectedInfo);
TestCanvas.Childrens.Add(mp);
}
}
TestGridUI:
public partial class TestGridUC : UserControl {
private TestClassWithInfo m_SelectedInfo;
public TestGridUC (ref TestClassWithInfo e)
{
InitializeComponent();
m_SelectedInfo = e;
}
}
TestClassWithInfo:
public class TestClassWithInfo
{
public Image imageTest;
public int intTest;
public TestClassWithInfo ()
{
m_img = null;
m_layer = 0;
}
}
I am aware of the existence of DependencyProperty, however, I could not get it to work.
A dependency property really is the way to go about it though:
public partial class TestGridUC : UserControl
{
public TestGridUC()
{
InitializeComponent();
}
public TestClassWithInfo Info
{
get { return (TestClassWithInfo)GetValue(InfoProperty); }
set { SetValue(InfoProperty, value); }
}
public static readonly DependencyProperty InfoProperty =
DependencyProperty.Register("Info", typeof(TestClassWithInfo), typeof(TestGridUC),
new PropertyMetadata(null /*or initialize to a default of new TestClassWithInfo()*/ ));
}
Now you can bind to that property from the xaml in your MainWindow:
<local:TestGridUC
Info="{Binding Info}"></local:TestGridUC>
If you need help with that part, as pr177 answered there are many tutorials on getting started with WPF with the MVVM pattern. The basics here would involve a view model object that contains a TestClassWithInfo public property that you bind to.
Have a look at the MVVM (Model-View-ViewModel) Pattern
There are many tutorials & introductions like that:
https://blogs.msdn.microsoft.com/ivo_manolov/2012/03/17/model-view-viewmodel-mvvm-applications-general-introduction/
or
https://social.technet.microsoft.com/wiki/contents/articles/32164.wpf-mvvm-step-by-step-2.aspx

C# Binding global static string to UWP Textblock

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.

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);
}
}
}
}

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