Binding property of static member of Application class - wpf

I have a class "Application" which has a static member(MainWindow, inherits from DXWindow) like the following:
public static MainWindow MainWindowInstance
I instantiate and display it:
MainWindowInstance = new MainWindow();
MainWindowInstance.ShowDialog();
MainWindow has a property:
private Thickness _localAttachmentsButtonsMargin = new Thickness(0, 0, 5, 0);
public Thickness LocalAttachmentsButtonsMargin {
get {
return _localAttachmentsButtonsMargin;
}
set {
_localAttachmentsButtonsMargin = value;
NotifyPropertyChanged("LocalAttachmentsButtonsMargin");
}
}
I need to access the LocalAttachmentsButtonsMargin property from many other controls (like UserControls) in xaml.
What I tried so far is:
--> Access it with x:Static:
Margin="{Binding Path=MainWindowInstance.LocalAttachmentsButtonsMargin, Source={x:Static Application.Current}}"
--> Giving the UserControl a x:Name ("UcTmp"), provide a getter in the UserControl and access the property from code behind
Margin="{Binding ElementName=UcTmp, Path=LocalAttachmentsButtonsMargin}}"
public Thickness LocalAttachmentsButtonsMargin {
get {
Application.MainWindowInstance.LocalAttachmentsButtonsMargin;
}
}
But none of them both seems to work. I think I need the xaml equivalent to the one line of code in the getter given above.
An additional requirement is that the property in MainWindow needs to be updatable. Ideally the solution is able to update the view which is represented throught the UserControl if the property will be updated though some code.

Since you can only bind to properties, you should define MainWindowInstance as such:
public static MainWindow MainWindowInstance { get; set; }
It cannot be a field:
public static MainWindow MainWindowInstance;

Related

How to bind to the dependency property of the dependency object?

The following is only a summary of the core of the problem.
I defined the class that has one dependency property is as below.
public class TestMap : DependencyObject
{
public bool TestProperty
{
get { return (bool)GetValue(TestPropertyProperty); }
set { SetValue(TestPropertyProperty, value); }
}
public static readonly DependencyProperty TestPropertyProperty =
DependencyProperty.Register("TestProperty", typeof(bool), typeof(TestMap), new PropertyMetadata(false));
}
Now I'm going to use the above class in the UserControl.
The following code is an example.
public class UserControl1 : Control
{
public TestMap TestMap { get; set; }
}
And I used the UserControl1 in the MainWindow like below.
<Window>
<Grid>
<local:UserControl1 />
</Grid>
</Window>
The above code works well but If I try to binding to the TestMap.TestProperty what I should do?
I tried the following code but it doesn't work. (Assume ViewModel is bound)
<Window>
<Grid>
<local:UserControl1 TestMap.TestProperty="{Binding ViewModelProperty}"/>
</Grid>
</Window>
The error message for the above code is as below image.
The error message is
"It can't found connectable 'TestProperty' property in the 'TestMap' format."
Thanks for reading.
You can't bind to a nested property, i.e. a property TestProperty of the object returned by the property TestMap, like this in XAML. It's not supported.
You should either move the TestProperty to the UserControl, set it programatically, or consider using an attached dependency property that is settable on any object of a specific type.

WPF custom grid with property to set with databinding

I am using WPF and I have a custom datagrid. What I would like to do is add a property (CustomGridCommands) to that grid which I can set in xaml from any view.
What I have at the moment is as follows (I have changed the code a bit to simplify it):
Custom grid C# code:
public class CustomWPFDataGrid : DataGrid, INotifyPropertyChanged
{
public static readonly DependencyProperty CustomContextMenuCommandsProperty =
DependencyProperty.Register("CustomContextMenuCommands",
typeof (ObservableCollection<WPFBaseCommand>),
typeof (CustomWPFDataGrid));
[Bindable(true)]
public ObservableCollection<WPFBaseCommand> CustomContextMenuCommands
{
get { return (ObservableCollection<WPFBaseCommand>) GetValue(CustomContextMenuCommandsProperty); }
set { SetValue(CustomContextMenuCommandsProperty, value); }
}
...
...
}
XAML code:
<common:CustomWPFDataGrid
ItemsSource="{Binding Path=ItemList}"
CustomContextMenuCommands="{Binding Path=CustomGridCommands, Mode=TwoWay}">
....
</common:CustomWPFDataGrid >
The object I have bound to the view that contains the grid is as follows:
public class TestViewModel
{
public ObservableCollection<TestDisplayViewModel> ItemList { get; set; }
public ObservableCollection<WPFBaseCommand> CustomGridCommands;
public TestViewModel()
{
... population of objects here
}
When I run this, and check the value of the property (CustomContextMenuCommands) in the datagrid, it is always null.
Any ideas what I am doing wrong?
EDIT
The setter of the "CustomContextMenuCommands" is never hit.
CustomGridCommands in your ViewModel is a field, View cannot use it. If you make it a public property, then it will become accessible. More details on what can be used as binding source can be found on MSDN - Binding Sources.
If using WPF 4.5, static properties can also be used for binding, as described in release notes.

How can I bind to a control in the MainWindow from another window in WPF?

Basically, my question appears already in the title:
When I have a MainWindow like following:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:easycache"
xmlns:map="clr-namespace:MapControl;assembly=MapControl.WPF"
x:Class="easycache.MainWindow"
x:Name="MainWindow1"
Title="easycache"
Height="600"
Width="850">
<map:Map Grid.Column="2"
Grid.Row="1"
Name="map"
IsManipulationEnabled="True"
LightForeground="Black"
LightBackground="White"
DarkForeground="White"
DarkBackground="#FF3F3F3F"
Center="0.0,0.0"
ZoomLevel="0.0"
TileLayer="{Binding Source={StaticResource TileLayersView}, Path=CurrentItem}" />
In My Window 2, I have:
<map:Map Name="map" IsManipulationEnabled="False"
LightForeground="Black" LightBackground="White" DarkForeground="White" DarkBackground="#FF3F3F3F"
Center="0.0,0.0" ZoomLevel="{Binding ?????}">
And I want to bind the ZoomLevel of the map in Window2 to the ZoomLevel of the map in my MainWindow. How can I achieve this?
Regards,
Ialokim
In your MainWindow codebehind create the a public property with a public get accessor, something like this:
private double _mapZoom;
public double MapZoom //I assume that ZoomLevel is of type double, if not use the correct type
{
get { return _mapZoom; }
set { _mapZoom = value; OnPropertyChanged("MapZoom"); }
}
You'll have to implement the INotifyPropertyChanged interface in your MainWindow (there is a million examples everywhere, no need to get into that here).
Then, bind your map's ZoomLevel property to the MapZoom property, like this:
<map:Map Name="map" ZoomLevel="{Binding RelativeSource={RelativeSource Self}, Path=MapZoom}, Mode=TwoWay"/>
For this to work, the DataContext of the Window must be the Window itself, in you need one line in the Window's constructor:
public MainWindow()
{
InitializeComponent();
this.DataContext = this; //add this line
}
You need a way for the second window to call the main window, so you can create a static property to return the current instance in the mainwindow:
private static MainWindow _instance;
public static MainWindow Instance
{
get
{
if (_instance == null)
_instance = new MainWindow();
else
_instance = this;
return _instance;
}
} //you have to make sure to create ONE instance of MainWindow before getting this property, and not to create more instances elsewhere
Now you'll have the MainWindow exposing a public property, which is bound to the map's zoom.
So, in your second Window create a property pointing at that instance of the main Window:
public MainWindow Main { get { return MainWindow.Instance; }
Finally, you can bind the zoom of the second map to the public property MapZoom which is a member of Main:
<map:Map Name="map2" ZoomLevel="{{Binding RelativeSource={RelativeSource Self}, Path=Main.MapZoom}, Mode=TwoWay"}">
In this binding "Main" is referring to the MainWindow instance through the public property of window2 (set the this.DataContext = this; in window2 too), and accessing the .MapZoom property.
By default, all objects you create in XAML are private, so you have to expose them explicitly with public properties with get/set accessors to make them accesible from outside.
Let me know if this works, I did not create an app to test the code. Regards!

Binding a Dynamic Application.Resource WPF - User Control

I have a custom class:
class CustomClass
{
public string Test { get; set; }
CustomClass()
{
this.Test = "";
}
}
I'm declaring this custom class on a Application.Resources like that:
<Application.Resources>
<local:CustomClass x:Key="myobj"/>
</Application.Resources>
This resource is the DataContext of a grid and the TextBox binds the Test property, like that:
<Grid DataContext="{DynamicResource myobj}">
<TextBox Text="{Binding Path=Test, Mode=TwoWay}"></TextBox>
</Grid>
Suddenly at run-time, I change the value of the resource
this.Resources["myobj"] = new CustomClass() { Test = "12456" };
I want the value referenced on TextBox be always the value of the object that is currently on "myobj" resource, and I want change automatically the value of the current object when the value of Text property of the TextBox is changed, because of this, I used the Mode=TwoWay, but it's not happening.
I used WPF Inspector and I saw when the resource value is changed, binds a new cleared object and not my created object
I'm new in WPF sorry my english and my unknowledge;
Regards,
EDIT 1
It works implementing the code posted by ethicallogics, thanks! But sorry if I wasn't clear before, when binds a new resource as below
this.Resources["myobj"] = new instance;
it works fine when it is called inside the same window that this resource was declared, unlike when I call this line inside a UserControl, it seems that the UserControl doesn't inherit the MainWindow Resources, how that really works ?
class CustomClass:INotifyPropertyChanged
{
private string _test;
public string Test
{
get
{
return _test;
}
set
{
_test = value;
Notify("Test");
}
}
CustomClass()
{
this.Test = "";
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private void Notify(string propName)
{
if(PropertyChanged!=null)
PropertyChanged(this,new PropertyChangedEventArgs(propName);
}
}
Use this class .I hope this will help.

Combobox.SelectedItem binding to nested property

I am doing a simple WPF application using MVVM and I am having trouble binding to the SelectedItem property of the combobox.
The setter of the bound property does not get called, and there is no output in the debug windows telling me it is not able to bind (I assume it is able to).
This is .NET 3.5, I made a small example that has the same problem.
In XAML:
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel>
<ComboBox IsDropDownOpen="False" IsReadOnly="False" ItemsSource="{Binding Path=Printers}" SelectedIndex="0" SelectedItem="{Binding Path=Printer.SelectedPrinter, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Name="cmbPrinters" />
</StackPanel>
</Window>
View code behind:
using System.Windows;
public partial class Window1 : Window
{
ViewModel viewmodel;
public Window1()
{
viewmodel = new ViewModel();
this.DataContext = viewmodel;
InitializeComponent();
}
}
View model:
using System;
using System.Collections.ObjectModel;
public class ViewModel
{
public ViewModel()
{
Printers = new ObservableCollection<string>() { "test", "test2" };
Printer = new PrinterViewModel();
}
public PrinterViewModel Printer { get; set; }
public ObservableCollection<string> Printers { get; set; }
}
PrinterViewModel:
using System.Windows;
using System.Diagnostics;
public class PrinterViewModel : DependencyObject
{
public string SelectedPrinter
{
get { return (string)GetValue(SelectedPrinterProperty); }
set
{
SetValue(SelectedPrinterProperty, value);
Debug.WriteLine("!!!!!! SelectedPrinter setter called");
}
}
public readonly DependencyProperty SelectedPrinterProperty =
DependencyProperty.Register("SelectedPrinter", typeof(string), typeof(PrinterViewModel), new UIPropertyMetadata());
}
Can anyone see what I am doing wrong here?
The problem here is that you have a misunderstanding about how the Silverlight dependency property system works.
When the value of a dependency property changes, Silverlight doesn't go through the properties you've defined (such as SelectedPrinter) to set the value of the dependency property. The Silverlight dependency property mechanism keeps track of all of the values of dependency properties, and when the value of one of these properties changes, Silverlight changes its value directly without calling your code to do so. In particular, it will not call your property setter. This should explain why your debugging message wasn't appearing.
The getter in a property that uses a dependency property, such as your SelectedPrinter property, should contain only a call to GetValue, and the setter should contain only a call to SetValue. You shouldn't add any code to the getter or setter, as doing this will not achieve what you want.
Furthermore, you are using dependency properties in the view-model layer. This is not where they are intended to be used. Dependency properties are only intended to be used in the view-layer. Your view-model classes should instead be implementing INotifyPropertyChanged rather than extending DependencyObject.
It is possible to bind two dependency properties together. This is permitted, and occasionally it comes in useful to wire together two dependency properties in the view-layer. In fact, the bindings in your example were working, which explains why you weren't getting any messages about problems with bindings.
why inherit from DependencyObject when doing mvvm?`
public class PrinterViewModel : INotifyPropertyChanged
{
private string _selected;
public string SelectedPrinter
{
get { return this._selected; }
set
{
_selected= value;
OnPropertyChanged("SelectedPrinter");
}
}
}
now your code should work

Resources