Pass view to viewmodel with datatemplate - wpf

I have a window named ParameterEditorView with a ParameterEditorViewModel as DataContext. In the ParameterEditorViewModel I have a list of ParameterViewModel. In the ParameterEditorView I have an ItemsControl whose ItemsSource is binded to the list of ParameterViewModel in the ParameterEditorViewModel. I need the ParameterViewModel to have a reference to the ParameterView (more on that later). In the Resources section of the ParameterEditorView I add the DataTemplate:
<DataTemplate DataType="{x:Type my:ParameterViewModel}" >
<my:ParameterView HorizontalAlignment="Left"/>
</DataTemplate>
So, how can I pass a reference of the ParameterView that is created to show the ParameterViewModel to it?
The reason I need the ParameterView in the ParameterViewModel is the following:
I have a TextBox whose Text property is binded to the PropertyModelView.Name property. But I want to display a default string when the Name is empty or Null. I've tried to set the property value to the default string I want when that happens but the TextBox.Text is not set in this scenario. I do something like this:
private string _name;
public string Name
{
get { return _name; }
set
{
if (value == null || value.Length == 0)
Name = _defaultName;
else
_name = value;
}
}
I've also tried to specifically set the TextBox.Text binding mode to TwoWay without success.
I think this is a defense mechanism to prevent an infinite loop from happening but I don't know for sure.
Any help on this front would also be highly appreciated.
Thanks,
José Tavares

{Binding } has a FallbackValue, btw.
Your question, it confuses me. I'd assume your PVM has a collection of PV's as a public property, which is bound within the UI. Also, I think you're mixing terms. Its Model-View-ViewModel where the ViewModel is the DataContext of the View, and the Model is exposed by the ViewModel via a public property. Sounds like if you're binding the window to a collection of ViewModels they are actually Models. It may seem pedantic, but getting your terms correct will help you research and ask questions.
Another solution would be to add a Converter to your Binding in combination with FallbackValue (I've had to do this, IIRC). That converter would be an IValueConverter that returns "DependencyProperty.UnsetValue" if the string is null or empty. I think this works sometimes because the TextBox will set the bound property to the empty string rather than null if the TB is empty. Here's a little sample to whet your whistle (not guaranteed to work; you need to debug this and tweak it):
public class ThisMightWorkConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
var temp = value as string;
if(string.IsNullOrWhiteSpace(temp))
return System.Windows.DependencyProperty.UnsetValue;
return temp;
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
return value; // you might need to change this
}
}

Related

How to know if binding has no target in case an object doesn't have that property

I have some classes. Some of them has Color Property, but one does not. I'm using same ListBox User Control for them. I want to hide ColorPicker for those classes that have no such property. I know, I can do a workaround and hide it if DataContext is of certain type, but I want to know if there is a way to check if the binding target isn't just null at a moment, but doesn't exist at all.
I used the proposed converter (returning true/false) with no result, but #mm8 proposal to set FallbackValue to false worked well.
You could specify a FallbackValue for a specific binding that the target property will be set the when the source property is not found: https://msdn.microsoft.com/en-us/library/system.windows.data.bindingbase.fallbackvalue(v=vs.110).aspx
Use a ValueConverter for the binding, and in the Convert method, check for UnsetValue:
<FrameworkElement Property="{Binding SomeProperty, Converter={StaticResource BindingExists}/>
and
public class BindingExists : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == DependencyProperty.UnsetValue)
{
// perhaps do something
return Binding.DoNothing;
}
else if (value == null)
{
// perhaps do something else
}
return value
}
// ...
You can then use DataTriggers to display different templates or whatever, in case of null vs. non-existent value.

XPath : Bind to last item of collection

Can I Bind TextBox.Text to last item of an ObservableCollection<string> ?
I tried this:
<TextBox Text={Binding XPath="Model/CollectionOfString[last()]"/>
But it doesn't bind.
Thank you.
Please try the method following,
1, use IValueConverter.
class DataSourceToLastItemConverter : IValueConverter
{
public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
IEnumerable<object> items = value as IEnumerable<object>;
if (items != null)
{
return items.LastOrDefault();
}
else return Binding.DoNothing;
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new System.NotImplementedException();
}
}
Then binding like this:
<Grid>
<Grid.Resources>
<local:DataSourceToLastItemConverter x:Key="DataSourceToLastItemConverter" />
</Grid.Resources>
<TextBox Text="{Binding Path=Model.CollectionOfString,Converter={StaticResource DataSourceToLastItemConverter}}"/>
</Grid>
It doesn't bind because you cannot use the XPath property on a-non XML data source; you have to use Path instead, and that property doesn't offer similar syntax. So you cannot directly bind to the last element of the collection unless you know the index of the last value. However there are a couple workarounds available:
Bind using a value converter
It's not difficult to write custom value converter that takes the collection and "converts" it to its last element. Howard's answer gives a barebones converter that does this.
Bind to the current item in the collection view
This is even easier to do, but it involves code-behind.
You can bind using Path=Model.CollectionOfString/ (note the slash at the end) if you have set the "current" item in the default collection view to be the last item in the collection. Do this inside your model:
// get a reference to the default collection view for this.CollectionOfString
var collectionView = CollectionViewSource.GetDefault(this.CollectionOfString);
// set the "current" item to the last, enabling direct binding to it with a /
collectionView.MoveCurrentToLast();
Be aware that if items are added to or removed from the collection, the current item pointer will not necessarily be adjusted automatically.

WPF binding tricky issue

I need some assistance to implement some data binding. My viewmodel exposes the following properties:
public List<string> ChosenFeatures {get;set;}
public Dictionary<string, double> AllFeatureCosts {get;set;}
"ChosenFeatures" will contain a subset of dictionary keys present in "AllFeatureCosts".
In the view I would like to render a series of TextBlocks, one for each item in "ChosenFeatures". Here's the tricky part:- the Text property of each TextBlock need to be bound to a value in the "AllFeatureCosts" dictionary, using the string in "ChosenFeatures" as the key to that dictionary item.
I would be grateful for any pointers on how to write the XAML to accomplish this.
Provide a ViewModel for the data, thats the reason to use MVVM in the first place.
class FeatureViewModel
{
public FeatureViewModel(MyViewModel aViewModel, string aKey)
{
mParent = aViewModel;
mKey = aKey
}
public string Value
{
get{return mParent.AllFeatureCosts[mKey];}
}
}
add a collection for your viewmodels to your main viewmodel
public ObservableCollection<FeatureViewModel> Features{ get; set; }
and initialize it somewhere
foreach(var feature in ChosenFeatures)
{
Features.Add(new VisualFeature(this, feature) );
}
from here you can also if necessary and if you have INotifyPropertyChanged properly implemented, raise any changes on the FeatureViewModels. You of course need to keep these collections in sync, which might be a bit of work.
Of course, your DataTemplate needs some adjustments aswell
<DataTemplate DataType="{x:Type FeatureViewModel}">
<TextBlock Text="{Binding Value}"/>
</DataTemplate>
First of all, I suppose you should use #Jay's approach and make ChosenFeatures Dictionary also.
However you can use Converter instead and pass your dictionary like a parameter while binding:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var key = (string)value;
var dictionary = (Dictionary<string, double>)parameter;
if (dictionary.ContainsKey(key))
return dictionary[key];
else
return null;
}
Instead of binding "ChosenFeatures" bind "AllFeatureCosts". We know that it will display complete list and we can then write a simple Multibinding visibility converter to display the items which are selected (in ChosenFeatures).
Note:
Depending on the size of the dictionary it may affect application performance...

How to resolve/control property name collision in XAML binding?

Interview Question
Phrased as:
If you have a property name collision, how would you specify the exact property to bind to in a Binding path expression (in XAML)?
I never faced this (property name collision) problem in any binding so far. With some reading I realized that this is possible in case I am binding to a overridden property because then I have two instances of this property (virtual in base, and overriden in derived) as far as resolution using Reflection is concerned. Which is what used by XAML.
Could there be any other case where XAML might face a property name collision?
Is there some support in API to handle/control that? (Instead of of course avoiding a collision)
Thanks for your interest.
Sounds like a complete nonsense to me. Unless they wanted to talk about bindings, using 'disjointed' sources like PriorityBinding and MultiBinding.
Frankly speaking I don't think overwritten properties can be involved into the matter as this is so much out of scope, you could equaly point out explicit interface implementations and many other things, which are clearly outside of WPF domain.
The best way I can think would be to use a ValueConverter. I don't think this really answers the question though since they're asking in a binding path expression, which I haven't seen to be possible. I'm not particularly fond of doing it this way because it feels like a hack, but it works at least for one way binding. Here's an example of how you might do it:
XAML:
<StackPanel Name="stack">
<StackPanel.Resources>
<loc:OverriddenMyPropertyConverter x:Key="BaseMyProperty"/>
</StackPanel.Resources>
<TextBox Text="{Binding Path=MyProperty}"/>
<TextBox Text="{Binding Mode=OneWay, Converter={StaticResource BaseMyProperty}}"/>
</StackPanel>
The DataContext of the StackPanel is an instance of MyClass. The first TextBox is bound to the MyClass.MyProperty property, and the second TextBox will be bound to the MyBaseClass.MyProperty property. Two way binding would be a bit more complex since the object actually being bound to the second TextBox is the MyClass object and not the MyProperty object.
Code:
class MyClass : MyBaseClass
{
string myProperty = "overridden";
public new string MyProperty
{
get { return myProperty; }
set { myProperty = value; }
}
}
class MyBaseClass
{
string baseProperty = "base";
public string MyProperty
{
get { return baseProperty; }
set { baseProperty = value; }
}
}
class OverriddenMyPropertyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (value as MyBaseClass).MyProperty;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

WPF Property Data binding to negate the property

Is there any way to change the value of property at runtime in WPF data binding. Let's say my TextBox is bind to a IsAdmin property. Is there anyway I can change that property value in XAML to be !IsAdmin.
I just want to negate the property so Valueconverter might be an overkill!
NOTE: Without using ValueConverter
You can use an IValueConverter.
[ValueConversion(typeof(bool), typeof(bool))]
public class InvertBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool original = (bool)value;
return !original;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
bool original = (bool)value;
return !original;
}
}
Then you'd setup your binding like:
<TextBlock Text="{Binding Path=IsAdmin, Converter={StaticResource boolConvert}}" />
Add a resource (usually in your UserControl/Window) like so:
<local:InvertBooleanConverter x:Key="boolConvert"/>
Edit in response to comment:
If you want to avoid a value converter for some reason (although I feel that it's the most appropriate place), you can do the conversion directly in your ViewModel. Just add a property like:
public bool IsRegularUser
{
get { return !this.IsAdmin; }
}
If you do this, however, make sure your IsAdmin property setter also raises a PropertyChanged event for "IsRegularUser" as well as "IsAdmin", so the UI updates accordingly.
If you specifically want to do this at XAML end (I am not sure the reason for that, unless you have 100s of similar operation of negate) there are only two ways 1) Using IValueConverter 2)write a XAML Markup Extension (Way too much work for this small task :))
Then the other obvious way is to write another property in your ViewModel , which can return the Negative of the IsAdmin property.
You can't bind to !Property, but you could create a new Binding with an appropriate IValueConverter and change out the entire Binding at runtime. The key is the BindingOperations class, which allows you to change the binding on a particular DependencyProperty.
public static void InvertBinding(DependencyObject target, DependencyProperty dp)
{
//We'll invert the existing binding, so need to find it
var binding = BindingOperations.GetBinding(target, dp);
if (binding != null)
{
if (binding.Converter != null)
throw new InvalidOperationException("This binding already has a converter and cannot be inverted");
binding.Converter = new InvertingValueConverter(); //This would be your custom converter
//Not sure if you need this step, but it will cause the binding to refresh
BindingOperations.SetBinding(target, dp, binding);
}
}
This should give you a general idea; I wouldn't use this for production code, as you'd probably want to generalize it to toggle the converter or whatever else you need to change out at runtime. You could also avoid changing the binding entirely by creating a new property you bind to that encapsulates this 'switching' logic. The last option is probably the best.
You can write a ValueConverter that automatically negates the input before returning it. Have a look at BenCon's blog for a short reading on value converters.

Resources