Binding property from a property - c#

how can I do a bind if the property to show is a property from a property, like this case:
Xaml:
<TextBox Text="{Binding log.Message}"/> ????
In the class defined as Datacontext, I declare a log variable:
public Log log = new Log();
the Log class:
public class Log : INotifyPropertyChanged
{
public static string Message{ get { return message; } }
....

Your question is a bit unclear to me, but i give it a shot:
If the DataContext is an instance of the Log class, and the property is non static. Than the proper binding would be
<TextBox Text="{Binding Message}"/>
From there you can easily nest your bindings. For example if Log would have an instance of a class
public class Log {
public MessageHandler Message {get;set;}
}
which would have a property LocalizedMessage, it would simply be
<TextBox Text="{Binding Message.LocalizedMessage}"/>
If you want to bind to a static property, which your Message property currently is:
<TextBox Text="{Binding Source={x:Static MyNs:Log.Message}, Path=.}"/>

You can't bind static properties to XAML. Only .Net 4.5 enables that, and even that with some work. see: WPF 4.5 – Part 9 : binding to static properties. You can find the way there.
If you can't use .Net 4.5, check out this SO thread for another workaround.

The problem with what you wrote is that Message is a static property, so you're not suppose to get it from the log object, but from the Log class:
<Window.Resources>
<local:Log x:Key="logClass"/>
</Window.Resources>
<TextBox Text="{Binding Source={StaticResource logClass}, Path=Message}"/

Related

Bind a property, inside DataTemplateItem in ListBox, to an outside object in MainPage

I got ListBox with DataTemplate, inside DataTemplate I got another ListBox, trying to bind it's Visibility to another object which is found in the MainPage
XAML:
<ListBox x:Name="RegistersListView" ItemsSource="{x:Bind registersList}">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="structures:Register">
<StackPanel>
<ListBox x:Name="FieldsListView" ItemsSource="{x:Bind fields_list}" Visibility="{x:Bind SomeVisibilityObjectIMain}">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="structures:Field">
<Button Content="{x:Bind name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
C#:
public sealed partial class HWTab : Page
{
public ObservableCollection<Register> registersList = new ObservableCollection<Register>();
public var SomeVisibilityObjectIMain;
public HWTab()
{
InitializeComponent();
InitData();
this.DataContext = hwType;
}
....
}
I need to bind to "SomeVisibilityObjectIMain" somehow, I tried to bind with ElementName or even make object static, but could not succeed.
My bindable object is more complex than the example here but solve this will give me the way for solution.
You could use {Binding} instead of x:Bind. This way you could add a x:Name="Page" to your page and then use this name in the inner binding:
{Binding ElementName=Page, Path=MyProperty}
For {Binding} to work however, MyProperty must be actually a property. From your sample code (which uses var which is also invalid) it seems it is just a plain field, so you will need something like:
public string MyProperty {get;set;}
To also get PropertyChanged notifications, you will need to add a backing field and trigger PropertyChanged event.
However, overall a better solution would be to include all information a DataTemplate needs into the actual items which are bound to it. That means - you would create a custom view model type for the items, which would include the information that you need to control visibility.

XAML binding to a codebehind class property collection is blank (WPF)

My codebehind defines a simple class with properties and a constructor as so:
public class Question
{
public string[] Answers
{
get; set;
}
public int CorrectAnswerIndex
{
get; set;
}
public Question(string[] answers, int correctIndex)
{
this.Answers = answers;
this.CorrectAnswerIndex = correctIndex;
}
}
There then exists a public object of that type that gets initialised in the window's constructor like so:
CurrentQuestion = new Question(
new string[] { "First", "Second", "Third", "Fourth" }, 2
);
I then have the following XAML in an attempt to print out all of the possible answers from said question.
<Grid Margin="150,150,150,150" DataContext="local:CurrentQuestion">
<ListBox ItemsSource="{Binding Answers}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
The local namespace is defined previously as the CLR namespace.
However, my list emerges entirely empty. There are no binding errors at runtime.
What's going on here? It seems a simple example that just won't run. I feel I've missed something "obvious."
This will look at ListBox.DataContext for a property named Answers, and try to use that for the ItemsSource.
<ListBox ItemsSource="{Binding Answers}">
ListBox.DataContext will be inherited from the parent Grid. Unfortunately, the grid's DataContext is a string, and strings don't have a property called Answers. So the Binding can't do anything and gives you null.
<Grid Margin="150,150,150,150" DataContext="local:CurrentQuestion">
<ListBox ItemsSource="{Binding Answers}">
XAML implicit conversions are a Do-What-I-Mean thing, thus a source of much confusion. There are times when you can put local:CurrentQuestion in an attribute value and have it be taken as a data type -- but this is not one of those times. And a data type isn't what you meant to provide anyway. You wanted a property by that name. But local: is a namespace, a literal CLR namespace like System.Windows.Controls, not a reference to an object.
Here's how the XAML in a UserControl can bind to a property of the UserControl. If it's a Window, change UserControl to Window.
<Grid Margin="150,150,150,150">
<ListBox
ItemsSource="{Binding CurrentQuestion.Answers, RelativeSource={RelativeSource AncestorType=UserControl}}">
I'm just guessing that CurrentQuestion is a property of the UserControl. Let me know if it's somewhere else.
You're also probably going to run into problems when you update CurrentQuestion, unless it's a dependency property. If it's a plain old CLR property like this, the UI won't be notified when its value changes:
public Question CurrentQuestion { get; set; }

Binding to DependencyProperty with no result

I have problem with binding to dependency property of my new control.
I decided to write some tests to examine this issue.
Binding from TextBox.Text to another TextBox.Text
XAML code:
<TextBox Name="Test" Text="{Binding ElementName=Test2, Path=Text, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Name="Test2" Grid.Row="2" />
The result is good - when I writing something in first TextBox -> second TextBox is updating (conversely too).
I created new control -> for example "SuperTextBox" with dependency property "SuperValue".
Control XAML code:
<UserControl x:Class="WpfApplication2.SuperTextBox"
...
Name="Root">
<TextBox Text="{Binding SuperValue, ElementName=Root, UpdateSourceTrigger=PropertyChanged}" />
</UserControl>
Code behind:
public partial class SuperTextBox : UserControl
{
public SuperTextBox()
{
InitializeComponent();
}
public static readonly DependencyProperty SuperValueProperty = DependencyProperty.Register(
"SuperValue",
typeof(string),
typeof(SuperTextBox),
new FrameworkPropertyMetadata(string.Empty)
);
public string SuperValue
{
get { return (string)GetValue(SuperValueProperty); }
set { SetValue(SuperValueProperty, value); }
}
}
Ok, and now tests!
Binding from TextBox.Text to SuperTextBox.SuperValue
<TextBox x:Name="Test1" Text="{Binding ElementName=Test2, Path=SuperValue, UpdateSourceTrigger=PropertyChanged}" />
<local:SuperTextBox x:Name="Test2" Grid.Row="2"/>
Test is correct too!
When I writing something in TextBox, SuperTextBox is updating.
When i writing in SuperTextBox, TextBox is updating.
All is ok!
Now a problem:
Binding from SuperTextBox.SuperValue to TextBox.Text
<TextBox x:Name="Test1"/>
<local:SuperTextBox x:Name="Test2" SuperValue="{Binding ElementName=Test1, Path=Text, UpdateSourceTrigger=PropertyChanged}" Grid.Row="2"/>
In this case, when I writing something in SuperTextBox, TextBox is not updating!
How can I fix this?
PS: Question is very very long, I am sorry for that, but i tried exactly describe my problem.
The reason why one works and the other doesn't is because the Text dependency property of TextBox is defined to bind TwoWay by default, while your dependency property SuperValue isn't. You need to use TwoWay-binding if you want the destination to update the source in addition to the source updating the destination.
To fix this, you can add FrameworkPropertyMetadataOptions.BindsTwoWayByDefault to SuperValue's metadata like so:
public static readonly DependencyProperty SuperValueProperty = DependencyProperty.Register(
"SuperValue",
typeof(string),
typeof(SuperTextBox),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)
);
Change binding mode into TwoWay.
Since in first two cases Test1 knows when it needs to update itself but not in third case. Only Test2 knows that when it should update in third case. That's why TwoWay mode is required in third case.
EDIT
First case is working since behind the scenes,xaml hook to
AddValueChanged event exposed by the PropertyDescriptor. For the
reason it's working refer to this link here.

Setting Label Text in XAML to string constant

I have a single string constant that I have to re-use in several different XAML layouts, so instead of duplicating it, I'd like to just bind it to a constant.
I have a class which defines the string in C#:
public static class StringConstants
{
public static string MyString { get { return "SomeConstant"; } }
}
I'd like to be able to set the value through XAML via something like the following:
<Label Content="{Binding local:StringConstants.MyString}"/>
Is this achievable? I've searched for examples, but I've only found samples that involve some tinkering in the code-behind and I'm wondering if there's a simpler, XAML-only solution if I know that I just need to set the value once based on a string value that will never change.
You are binding to a static member so you should use x:Static Markup Extension:
<Label Content="{Binding Source={x:Static local:StringConstants.MyString}}"/>
According to #H.B.'s comment it's not necessary to use Binding so it's simpler to use:
<Label Content="{x:Static local:StringConstants.MyString}"/>
Put the public static string MyString in your App.xaml.cs. Then you can reference it as follows.
Content="{Binding Source={x:Static Application.Current}, Path=MyString}"
In the case that you have a constant inside of a non-static class, this doesn't work.
My solution for binding to a constant inside of a view model (MVVM).
It uses a getter property with less code for wrapping.
// view model
public const string MyString = "abc";
public string MyStringConst => MyString;
.
<!-- WPF -->
<Label Content="{Binding MyStringConst, FallbackValue='abc'}" />
The FallbackValue is used for the Designer preview.

Setting ViewModel's Property from XAML

I have some UserControl, It's DataContext is binded to the ViewModel,
How to set a ViewModel's property from XAML? Is it possible?
UPD :
Sorry for being not very clear,
I'm trying to get something like this :
UserControl's DataContext is binded to ViewModel, I need to set ViewModel's property to something (let's say, UserControl's Width property).
Is it possible?
UPD2: It seems to be not possible.I know about TwoWay binding mode, etc, thing I wanted to do - to set ViewModel's property to UserControl's one
This example should be very clear
<Set Property={Binding SomePropertyOnViewModel}
Value={Binding RelativeSource={RelativeSource Self},
Path=SomePropertyOnUserControl}>
I am not sure whether I understand the question exactly.
But here is an example.
It will:
Create a view model of type ExampleViewModel inside the user control by setting the user
controls DataContext property in xaml
Create a text box in xaml and bind it to the view models
TextInViewModel string property.
Set up the usual INotifyPropertyChanged interface (this was extracted to the base class ViewModelBase)
Create the view model in xaml and set the user controls data context to it:
<UserControl x:Class="MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test"
xmlns:viewModel="clr-namespace:ViewModels">
<UserControl.DataContext>
<viewModel:ExampleViewModel/>
</UserControl.DataContext>
<StackPanel Orientation="Horizontal" >
<Label>Enter Text here: </Label>
<TextBox Text="{Binding TextInViewModel}"></TextBox>
</StackPanel>
</UserControl>
ViewModel:
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
}
public class ExampleViewModel : ViewModelBase
{
/// <summary>
/// Property bound to textbox in xaml.
/// </summary>
public String TextInViewModel
{
get { return _textInViewModel; }
set
{
_textInViewModel= value;
RaisePropertyChanged("TextInViewModel");
}
}
private string _textInViewModel;
/// <summary>
/// Constructor.
/// </summary>
public ExampleViewModel()
{
}
}
Binding works both ways: i.e. from source (e.g. viewmodel) to target (e.g. usercontrol) and from target back to source.
You specify the direction via Mode of binding.
Following are the BindingModes:
TwoWay
OneWay
OneTime
OneWayToSource
In your case, if you want to bind width property of usercontrol to the TheWidth property of ViewModel:
Case A:
Want to bind in both directions, use Mode=TwoWay
<UserControl Width="{Binding TheWidth, Mode=TwoWay}">
<!-- your rest of code -->
</UserControl>
Case B:
Want to bind only from usercontrol to viewmodel, use Mode=OneWayToSource
<UserControl Width="{Binding TheWidth, Mode=OneWayToSource}">
<!-- your rest of code -->
</UserControl>
XAML
<UserControl.DataContext>
<vm:ViewModel/>
</UserControl.DataContext>
I prefer the ViewModel Locator approach (this is like a service locator pattern for ViewModel).
Because as soon as your ViewModel has constructor parameters, you are either tightly coupled, or you can't use the above described xaml way....
There are many ViewModel-Locator ways. One is described here using MEF and silverlight.
http://johnpapa.net/simple-viewmodel-locator-for-mvvm-the-patients-have-left-the-asylum
here is another one:
http://brendan.enrick.com/post/Wire-up-your-ViewModels-using-a-Service-Locator.aspx
Well, you bind your UI elements to them:
<UserControl Width="{Binding Path=DisplayWidth, Mode=OneWayToSource}">
<Grid>
<TextBox MinWidth=100 Text="{Binding MyProperty}"/>
</Grid>
</UserControl>
assuming a view model like this:
class ViewModel
{
public string MyProperty { get; set; }
public int DisplayWidth { get; set; }
}
Through binding my dear friend..
for example: (Assuming in your context)
If you have class "Person" and your person has a Name and SurName public property and you want to bind it to a textbox. You do the following:
<TextBox Text="{Binding Path=Name}" />
This only works if the name is your public property, it is best practice to make you object ( in this case Person) as a public property and use the Path parameter differently.
Example:
<TextBox Text="{Binding Path=Person.Name}" />
It does clutter your code way less, then to make a property in your viewmodel for every property of any object in your viewmodel.
"How to set a ViewModel's property from XAML? Is it possible?"
So, that seems to be not possible, max you can accomplish - two-way binding, which is, unfortunately not I wanted.
All in all it's rather bad design than a problem

Categories