So I have a ProgressRing and a TextBlock and I am trying to implement this basic hack, which is to display both elements when TextBlock's Text gets assigned a value (anything other than null), else both elements should hide when TextBlock's Text is null.
My Xaml looks like below. TextBlock's Text is binded to MessageForProgressRing and its Visibility is binded to both MessageForProgressRing and TargetNullValue. Same for me ProgressRing:
<StackPanel Panel.ZIndex="100" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center">
<mahControls:ProgressRing Height="50" IsActive="True" Width="50" Visibility="{Binding MessageForProgressRing, TargetNullValue=Collapsed, FallbackValue=Visible}" Foreground="White" Margin="0,0,0.2,0" />
<TextBlock Text="{Binding MessageForProgressRing}" Visibility="{Binding MessageForProgressRing, TargetNullValue=Collapsed, FallbackValue=Visible}"/>
</StackPanel>
Then, in code behind I just trigger the property and assign it a value on some button event handlers:
private void closeApplicationButtonTask()
{
((CaptureViewModel)DataContext).MessageForProgressRing = "Closing... ";
Application.Current.MainWindow.Close();
}
However, in my ViewModelBase (the parent of all my view models) it pops an error on OnPropertyChanged saying:
Requested value 'Closing...' was not found.
I think I need a converter because Visibility is binded to Closing... right? If yes how can I achieve it?
P.S I couldn't do it in OnPropertyChanged because I don't see the value to assign it. Also I don't think it is a good idea since it gets called big time before, during and after the execution.
I usually prefer to solve this problem by having a boolean property in my view model (e.g. HasMessageForProgressRing or IsProgressRingVisible). It's usually a more general-purpose solution. Then you can use a BooleanToVisibilityConverter.
If you truly want to implement a converter, just create a class that implements IValueConverter. The Convert implementation of this should be a piece of cake for your simple use case. ConvertBack isn't necessary in most cases (and won't be in yours). It would look something like this:
public class NullToCollapsed : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Related
If I have a TextBox like this:
<TextBox Text="{Binding Voltage, StringFormat={}{0} kV}" />
and the property Voltage is e.g. 50, I get "50 kV" in my TextBox. This is what I intended.
But now, if the user wants to enter a new value say 40 and types "40 kV" he gets a red border, because there is a FormatException converting back the value.
System.Windows.Data Error: 7 : ConvertBack cannot convert value '40 kV' (type 'String'). BindingExpression:Path=Voltage; DataItem='VMO_VoltageDefinition' (HashCode=19837180); target element is 'TextBox' (Name=''); target property is 'Text' (type 'String') FormatException:'System.FormatException: Die Eingabezeichenfolge hat das falsche Format.
I don't think that the users of my program will accept this.
So am I doing something wrong or is this feature just not usable in a reasonable way with a TextBox?
I suggest usage of converter:
<TextBox Text="{Binding Voltage, Converter={StaticResource VoltageToString}}" />
Where:
<Window.Resources>
<mstf:VoltageToStringx:Key="VoltageToString" />
</Window.Resources>
And codebehind:
public class VoltageToString: IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return ((int)value).ToString() + " kV";
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return int.Parse((string).Replace(" kV",""));
}
}
That is just an basic example, but you should get the idea how to make it more sophisticated.
StringFormat is property of Binding markup extension and theoretically can be used in any binding. However, mostly it makes sense with one-way bindings only.
You are right, that stringformat does not makes much sense in TextBox.
You can workaround it by converter as David suggested, but I recommend you to display the unit in TextBlock outside TextBox:
<DockPanel>
<TextBlock Text="kV" DockPanel.Dock="Right />
<TextBox Text="{Binding Voltage}" />
</DockPanel>
This feels more natural and gives much better user experience.
alternatively, you can create custom control derived from TextBox with new property called Unit or Description, or whatever and modify the control templeate so it will show the unit. Then the final markup could look like this:
<my:TextBox Text="{Binding Voltage}" Description="kV" />
<GroupBox x:Name="groupBox" Header="Operating System" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Height="74" Width="280">
<StackPanel>
<RadioButton GroupName="Os" Content="Windows 7 (64-bit)" IsChecked="True"/>
<RadioButton GroupName="Os" Content="Windows 7 (32-bit)" />
</StackPanel>
</GroupBox>
I have several radio button groups in my application
How can I access which one has been checked in the Code-Behind using C#?
Is it absolutely necessary to use x:Name= on each RadioButton or is there a better way?
Code samples always appreciated
Yes! There is a better way, its called binding. Outside of binding, you are pretty much stuck (I can imagine handling all the checked events separately, and assigning to an enum, but is that really better?)
For radio buttons, you would typically use an enum to represent all the possible values:
public enum OsTypes
{
Windows7_32,
Windows7_64
}
And then bind each of your radio buttons to a global "selected" property on your VM. You need a ValueEqualsConverter for this:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.Equals(parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ((bool)value) ? parameter : Binding.DoNothing;
}
And then your radio buttons look like:
<RadioButton Content="Windows 7 32-bit"
IsChecked= "{Binding CurrentOs,
Converter={StaticResource ValueEqualsConverter},
ConverterParameter={x:Static local:OsTypes.Windows7_32}}"
Of course, you have a property in your VM:
public OsTypes CurrentOs {get; set;}
No x:Name, complicated switch statements, or anything else. Nice, clean, and well designed. MVVM works with WPF, use it!
First off, let me state I am an amateur when it comes to wpf. I am trying to create an a collapsing/expanding type action for a wpf button, meaning when a user clicks a button, I would like the button selected to expand a new list of buttons beneath it. This is meant to be the navigation type for the web-enabled application. I would also like to create a collapsing interaction when the button is pressed again on an opened list.
There is a default control for this in WPF, named the Expander. If you want to change the appearance or the animations you could look into templating and styling of WPF. By default this control should meet most of your requirements.
My first idea is to use a ToggleButton and bind its IsChecked property to a the visibility of the element you want to show. You would need a converter then to convert the boolean Checked value to a Visibility value. Here is an example:
<Grid>
<Grid.Resources>
<BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
</Grid.Ressources>
<ToggleButton x:Name="toggleButton" Content="Toggle" />
<Grid Visibility={Binding IsChecked, ElementName=toggleButton, Converter={StaticResource BoolToVisibilityConverter}}>
<!-- place your content here -->
</Grid>
</Grid>
The converter is a class implementig IValueConverter:
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool i = value is bool ? (bool) value : false;
return i ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Just use a ToggleButton and bind the visibility of the section to its IsChecked state as normally done in the Expander control (which you of course could just use instead).
I have binding on some command:
<Button Command="{Binding Save}" />
Save is command of some object that can be selected from list.
In initial state there is no any selected object so binding does not work and CanExecute does not invoked. How can i disable this button using MVVM?
Solution: WPF/MVVM: Disable a Button's state when the ViewModel behind the UserControl is not yet Initialized?
Guys, thanks for your answers and sorry for duplication of question.
Define a command that always return false to CanExecute. Declare it at a global position such as in your App.Xaml. you can specify this empty-command then as the FallbackValue for all your command bindings you expect a null value first.
<Button Command="{Binding Save,FallbackValue={StaticResource KeyOfYourEmptyCommand}}" />
You could create a trigger in XAML that disables the Button when the command equals x:Null.
An example can be found in the answer to this question: WPF/MVVM: Disable a Button`s state when the ViewModel behind the UserControl is not yet Initialized?
I'm not sure you'll be able to achieve this. However, an alternative would be to initialise the Command object initially with a basic ICommand where CanExecute simply returns False. You could then replace this when you're ready to put the real command in place.
Have a look at the Null Object Pattern
Create a NullToBooleanConverter and bind the IsEnabled property to the command, running it through the converter:
class NullToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then
<UserControl.Resources>
<Extentions:NullToBooleanConverter x:Key="NullToBooleanConverter" />
</UserControl.Resources>
<Button Content="Hello" IsEnabled="{Binding Save, Converter={StaticResource NullToBooleanConverter}}" />
I'm building a wp7 app in Silverlight. I have some content that gets loaded asynchronously, and messages that indicate that loading is not yet done. I'd like to have the loading messages disappear as soon as the content's list box is not empty. Is it possible to do this just in XAML? Something like binding the Visibility property to StoryListBox.ItemsSource.IsEmpty?
StoryListBox is populated by having its ItemsSource set to an observable collection after the data is available.
<TextBox x:Name="LoadingMessage" Text="Loading..." Grid.Row="0" />
<ProgressBar x:Name="LoadingProgress" IsIndeterminate="True" Style="{StaticResource PerformanceProgressBar}" />
<ListBox x:Name="StoryListBox" Grid.Row="0" />
Update: I tried the following, but it doesn't work:
<StackPanel x:Name="Loading" Grid.Row="0" Visibility="{Binding StoryListBox.ItemsSource.IsEmpty, Converter={StaticResource visibilityConverter}}">
<TextBox Text="Loading..." />
<ProgressBar IsIndeterminate="True" Style="{StaticResource PerformanceProgressBar}" />
</StackPanel>
<ListBox x:Name="StoryListBox" Grid.Row="1" />
The Loading stack panel never collapses.
You seem to have answered your own question. Yes, you can simply bind the Visibility (or Busy/IsBusy on a BusyIndicator control to some attribute of another control).
If the specific property you want to bind to is not a bindable property, simply bind to the other control and customise the converter to get the member property you want. If you have specific code examples, just post them and I can post a more specific solution.
The usual problem is that the types (for visibility) are incompatible with boolean values, so you need to specifier a converter in the binding. Google for Silverlight VisibilityConvertor (they are a dime a dozen). Here is mine:
namespace Common.ValueConverters
{
using System;
using System.Windows;
using System.Windows.Data;
public class VisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool?)
{
if (string.IsNullOrEmpty((string)parameter))
{
return (value as bool?).Value ? Visibility.Visible : Visibility.Collapsed;
}
else
{
return (value as bool?).Value ? Visibility.Collapsed : Visibility.Visible;
}
}
throw new ArgumentException();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
A use of the converter would look like:
<Grid Visibility="{Binding ShowDualView, Converter={StaticResource VisibilityConverter}}">
But quite frankly you are better of with a BusyIndicator control bound to an IsBusy property:
<Controls:BusyIndicator IsBusy="{Binding IsBusy}">
Just put it around the controls to you want to have hidden by the busy display.