WPF Binding FallbackValue set to Binding - wpf

Is there a way to have another binding as a fallback value?
I'm trying to do something like this:
<Label Content="{Binding SelectedItem.Name, ElementName=groupTreeView,
FallbackValue={Binding RootGroup.Name}}" />
If anyone's got another trick to pull it off, that would be great.

What you are looking for is something called PriorityBinding (#6 on this list)
(from the article)
The point to PriorityBinding is to
name multiple data bindings in order
of most desirable to least desirable.
This way if the first binding fails,
is empty and/or default, another
binding can take it's place.
e.g.
<TextBox>
<TextBox.Text>
<PriorityBinding>
<Binding Path="LastNameNonExistant" IsAsync="True" />
<Binding Path="FirstName" IsAsync="True" />
</PriorityBinding>
</TextBox.Text>
</TextBox>

If you run into problems with binding to null values and PriorityBinding (as Shimmy pointed out) you could go with MultiBinding and a MultiValueConverter like that:
public class PriorityMultiValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values.FirstOrDefault(o => o != null);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Usage:
<TextBox>
<TextBox.Text>
<MultiBinding Converter="{StaticResource PriorityMultiValueConverter}">
<Binding Path="LastNameNull" />
<Binding Path="FirstName" />
</MultiBinding>
</TextBox.Text>
</TextBox>

Under what conditions would you like it to use the Fallback value? How would you determine that a binding has failed? A binding is still valid even if it's bound to a null value.
I think a good bet may be to use a converter to convert to a default value if the binding returns null. I'm not sure how you could default to another bound value though.
Check out converters here

Related

Set Multibing for text - get and set

I would like to bind my TextBox.Text to two different sources.
I have 2 ViewModels, one is general ViewModel and one is more specific (which is inherit from its parent).
Both ViewModels has a property called "Hotkey".
I would like to bind my TextBox.Text so it will get the value from the general ViewModel and set it to the specific ViewModel.
I tried the following:
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center" Foreground="#000">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource test}">
<Binding Path="DataContext.Hotkey" RelativeSource="{RelativeSource AncestorType={x:Type MetroStyle:MetroWindow}}" Mode="OneWay" />
<Binding Path="Hotkey" Mode="OneWayToSource"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
It does get the value from the general ViewModel, but it doesn't set its value to the specific one (which inherits from the parent)
I believe the problem may be in the Converter you used for MultiBinding, I've just tried a simple demo and looks like that Converter should be implemented like this:
public class TestConverter : IMultiValueConverter
{
private bool justConvertedBack;
object IMultiValueConverter.Convert(object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
if (justConvertedBack) {
justConvertedBack = false;
return Binding.DoNothing;
}
return values[0];
}
object[] IMultiValueConverter.ConvertBack(object value, Type[] targetTypes,
object parameter, System.Globalization.CultureInfo culture)
{
justConvertedBack = true;
return new object[] {null, value};
}
}
It happens that after the ConvertBack has been done, the Convert will be triggered and keep the Text of your TextBox unchanged (although you tried deleting/modifying it before). So we need some flag justConvertedBack here to prevent that from occurring.
Currently changing the source from the general ViewModel will change the TextBox's Text but does not update the source from the specific ViewModel. However if setting/typing some value for the TextBox's Text will update the source from the specific ViewModel but won't reflect that value back to the source from the general ViewModel. I hope that behavior is what you want.

Converting controls from MM to Inches in XAML

I have a lot of controls in my XAML in the form of
TextBlock: TextBox.
So for example:
XSize(mm):25.4
YSize(mm):50.8
etc
Now when the user clicks on an option to use imperial units I want to change all textBlocks + textBoxes to something like
XSize(in):1
YSize(in):2
etc
What is the best way to go about this ?
Try to use Converters. Create a MM to Inch converter and use that converter to change the values when the user inputs a value on the certain value.
Sample usage of a converter. You have to define a static resource under the resources of your view for the controls you want to use the converter with
MainWindow.xaml
<Window.Resources>
<spikes:Convertaaa x:Key="Convertaaa" />
</Window.Resources>
<ComboBox x:Name="OptionsToChoose"/>
<TextBox>
<TextBox.Text>
<MultiBinding Converter="{StaticResource Convertaaa}">
<Binding ElementName="OptionsToChoose" Path="SelectedValue"/>
<Binding RelativeSource="{RelativeSource Self}" Path="Text"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
Converter.cs
public class Convertaaa : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
//Implement conversion here. values[0] will give you the selected option values[1] will give you the value to convert, then do a return
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
This assumes that you do know MVVM pattern in WPF and Bindings. If you are doing behind the code, then you probably want to hook up to the TextChanged of your TextBox and instantiate a new Converter Class and call the Convert method and set the Text property of the Textbox.

WPF content binding - want to display two attributes of a ListView in a StatusBarItem

I have a StatusBarItem which I want to display the following message at any given moment "X/Y" where X is the number of the row of the currently selected element, and Y is the row count.
Now, if I use the Content="{Binding ElementName=lvTabela, Path=SelectedIndex}" code in xaml I am able to get the first attribute to display, but I'm not sure how I can get both.
I suppose I could always use two StatusBarItem elements next to each other, but I'd like to learn how to do this as well.
Oh, and while we're at it, how would I go about incrementing the selected index? Basically, instead of -1 to rowCount-1 I'd like it to display 0 to rowCount. I've seen people using the formatter to add additional text to their data binding, but I'm not sure how I can manipulate the data like that.
You got 2 options:
Either set the Content of the StatusbarItem a TextBlock to use StringFormat with MultiBinding, Something like:
<StatusBarItem>
<StatusBarItem.Content>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}/{1}">
<MultiBinding.Bindings>
<Binding ElementName="listView"
Path="SelectedIndex" />
<Binding ElementName="listView"
Path="Items.Count" />
</MultiBinding.Bindings>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StatusBarItem.Content>
</StatusBarItem>
or use a Converter on the MultiBinding to not have to use the TextBlock
<Window.Resources>
<local:InfoConverter x:Key="InfoConverter" />
</Window.Resources>
...
<StatusBarItem>
<StatusBarItem.Content>
<MultiBinding Converter="{StaticResource InfoConverter}">
<MultiBinding.Bindings>
<Binding ElementName="listView"
Path="SelectedIndex" />
<Binding ElementName="listView"
Path="Items.Count" />
</MultiBinding.Bindings>
</MultiBinding>
</StatusBarItem.Content>
</StatusBarItem>
and InfoConverter.cs:
class InfoConverter : IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
return values[0].ToString() + "/" + values[1].ToString();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
StatusBarItem expects an object, when StringFormat returns a string and hence why we cannot use StringFormat with the MultiBinding without the TextBlock which can take a string in it's Text field.
As for your second question on how to increment the SelectedIndex value, you can do that with the Converter easily,
Just switch the Convert(...) function in InfoConverter.cs to
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
return (System.Convert.ToInt32(values[0]) + 1).ToString() + "/" + values[1].ToString();
}

Hide multibinding stringformat when binding returns null

I was looking to bind a timespan property to a textblock,which seems to be resolved with
the help of this post
Now i want to hide StringFormat when data is null.From the thread if i use a mutibinding with a stringformat and if my data is null then the stringformat displays just a ":"
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}:{1}">
<Binding Path="MyTime.Hours" TargetNullValue={x:Null}/>
<Binding Path="MyTime.Minutes" TargetNullValue={x:Null}/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
How could i hide the ":" if data is null
I'm basically answering the same thing as Nicolas Repiquet here but anyway..
It feels like there is a part missing in the framework here. There is no way (as far as I know) to make a MultiBinding use a FallbackValue without a Converter. Using this approach will probably set you back to square 1 since your last question was about a better approach then using a Converter :)
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}:{1}"
Converter="{StaticResource FallbackConverter}"
FallbackValue="">
<Binding Path="MyTime.Hours" />
<Binding Path="MyTime.Minutes" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
And the Converter basically does what you "should" be able to use a Property for
public class FallbackConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
foreach (object value in values)
{
if (value == DependencyProperty.UnsetValue)
{
return DependencyProperty.UnsetValue;
}
}
return values;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Take a look at multibinding fallback value here.

WPF : Conditional templating of textblock

I have a bunch of textblocks in an itemscontrol... I need to know how can I underline the text in the textblock based on whether the text is available in a list in the data model..
Sounds very simple to me...but I have been googling since the past 8 hrs...
Can I use datatriggers and valueconverters for this purpose? If yes, then how can I execute the method which lies in the viewModel (the method which helps me to check whther a given a text exists in the data model list)...
Even if I go for conditional templating....how do I access the list which lies in my model (the viewmodel can fetch it...but then how do i access the viewmodel?)..
This should be a fairly easy thing to do...Am I really missing something very simple here?? :)
I am following the MVVM pattern for my application..
One way is to use a multivalueconverter which is a class that implements IMultiValueConverter. A multivalueconverter allows you to bind to several values which means that you can get a reference to both your viewmodel and the text of your TextBlock in your valueconverter.
Assuming that your viewmodel has a method called GetIsUnderlined that returns true or false indicating whether or not the text should be underlined your valueconverter can be implemented along these lines:
class UnderlineValueConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var viewmodel = values[0] as Window1ViewModel;
var text = values[1] as string;
return viewmodel.GetIsUnderlined(text) ? TextDecorations.Underline : null;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
You can use this valueconverter in the following way for a TextBlock:
<Grid x:Name="grid1" >
<Grid.Resources>
<local:UnderlineValueConverter x:Key="underlineValueConverter" />
</Grid.Resources>
<TextBlock Text="Blahblah">
<TextBlock.TextDecorations>
<MultiBinding Converter="{StaticResource underlineValueConverter}">
<Binding /> <!-- Pass in the DataContext (the viewmodel) as the first parameter -->
<Binding Path="Text" RelativeSource="{RelativeSource Mode=Self}" /> <!-- Pass in the text of the TextBlock as the second parameter -->
</MultiBinding>
</TextBlock.TextDecorations>
</TextBlock>
</Grid>

Resources