0

In a simple MVVM application, I can add a binding to some property in the view. The property may access another object and return a property value from it. Say I want to display the active project in the view. If no project is active, a special note shall be displayed.

Now when the project is renamed, the name should be updated in the view. If I just returned the project's name in the property, it won't be updated of course.

So I thought I could just bind the view to another binding created in the property, that should forward the PropertyChanged event and update the view accordingly. But I see "System.Windows.Data.Binding" instead of the intended result of the binding like "Project: XYZ".

The project can be renamed anywhere so I'd like to avoid raising the PropertyChanged event for this ViewModel on my own from there. Things should be a little smarter on their own and not need to be pushed from everywhere (which you often forget at least once when things get more complex).

Here's the code:

XAML View:

<TextBlock Text="{Binding ActiveProjectName}"/> 

C# ViewModel:

public object ActiveProjectName { get { if (ActiveProject != null) { // This works but won't update automatically: //return "Project: " + ActiveProject.Name; // This does not work at all: return new Binding("Name") { Source = ActiveProject, StringFormat = "Project: {0}" }; } return "(No active project)"; } } 

Is that possible at all and how does it work right?

2 Answers 2

5

Unless the ActiveProject property is private or protected, use FallbackValue in xaml instead of if(ActiveProject != null) in code behind

example

<TextBlock Text="{Binding ActiveProject.Name,StringFormat=Project: {0},FallbackValue=(No active project)}"/> 

using PriorityBinding to have conditional binding

<TextBlock> <TextBlock.Text> <PriorityBinding FallbackValue="(No active project)"> <Binding Path="ActiveProject.Name" StringFormat="Project: {0}"/> <Binding Path="SomeOtherProject.Name" StringFormat="Other Project: {0}" /> </PriorityBinding> </TextBlock.Text> </TextBlock> 

In the above example PriorityBinding will first attempt to bind to ActiveProject, the Name property will then be used to resolve the value. If that is not available ie. null, then it will attempt to bind to SomeOtherProject. To resolve the value as per binding, if that result is also null, then the FallbackValue will be used as the value of TextBlock's Text property.

Sign up to request clarification or add additional context in comments.

6 Comments

Okay, this may work in the simple situation. But it can be more complex where the property code needs to make more decisions.
you may perhaps use priority bindings if decision is just checking nulls. or you may simply fire PropertyChanged once ActiveProjectName is supposed to be updated.
So you say it is not possible to return another Binding in a property that is bound to?
I did not mean to say that but once the binding is returned from the getter it will be always used for providing the value. In that case getter will not be called again and the condition if (ActiveProject != null) will not get evaluated. so it will not meet with what you expect. it applies to other part of the getter as well which returns a string instance.
I know that the VM property is only read once (and then when a change is notified). But the XAML object's (FrameworkElement or UIElement) property also does not contain the data itself but a Binding instance instead. That Binding resolves the data (once). It can be updated manually but that's ugly, too. So I thought I could add a second level of indirection and let the first Binding point to another one that can be notified of changes to the actual property and trigger an update on its own.
|
0

No no no no no! ;D

Use a IValueConverter for this! http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter(v=vs.110).aspx

Typically I would keep an ActiveProject/ActiveWhatever property that I would set in the code somewhere, rather than creating crazier and crazier bindings, complicating both your markup and code.

Also be sure to implement INotifyPropertyChanged on all your entities, you only have a getter hhere. Setting up bindings is code is something you should really avoid! (DO vm's and DP's in them is the bleh).

Datamodel / Entity:

public class ActiveProject : INotifyPropertyChanged { private string name; public String Name { get { return name; } set { if (value == name) return; name = value; OnPropertyChanged(); // Signals the UI that the property value has changed, and forces a re-evaluation of all bindings for this property } } public event PropertyChangedEventHandler PropertyChanged; [NotifyPropertyChangedInvocator] // No R#? Shame on you and comment this line out protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } } 

Converter:

public class YourConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { // Do whatever you want here ActiveProject project = value as ActiveProject; return project == null ? project.Name : "Whatever"; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { // Convert back if needed throw new NotImplementedException(); } } 

XAML:

... <xx.Resources> <local:YourConverter x:Key="YourConverter"/> <!--Define your converter as a staticresource, given that local is the namespace you have define. Alt+Enter with R# --> <xx.Resources> ... <TextBlock Text="{Binding ActiveProject, Converter={StaticResource local:YourConverter}}"/> 

3 Comments

When Project.Name is changed, how does the binding to Project know this? The reference to the Name property is only in the converter code which is invisible to XAML. (Only the property "Name" is notified for. But that's not the target of the binding.) While I understand your idea, I don't see how the answer solves my problem.
@LonelyPixel I was a bit fast there, but you are making this complicated, and complicated is bad. As you state ActiveProject must change (IE change the whole prop in code as I tried to explain, not very well above), rather than clutter your code. Now it's just the name, later it's something else... For now the solution above works for you. But on a general (off experience, been there done that) basis, you are digging your self into a hole here. ActiveProject = ProjectA; ... ActiveProject = ProjectB.. simple as cake!
What I meant is the case where not the entire ActiveProject instance is replaced and re-assigned, but a property (like the Name) of the current instance changes. The value converter will never know that. Your solution simply won't update when a project changes its name.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.