I have a main program window with a row of shortcut buttons along the bottom. I'm trying to make the Visibility of these selectable through a separate Settings window using CheckBoxes, and then store that state in the UserSettings of the program so when it's opened again the previous setting will be remembered. I've found and used an IValueConverter to implement this, and I know the Setting is saved as the CheckBox itself retains it's value.
The problem is that clicking the CheckBox does not affect the Visibility of the button. It works within the Settings window itself, but I can't seem to figure out how to make it work between different windows.
I have tried implementing both the UpdateSourceTrigger & NotifyOnSourceUpdated flags to no avail. Can anybody see the problem?
Main Window XAML
<WrapPanel.Resources>
<main:BooleanToHiddenVisibility x:Key="boolToVis" />
</WrapPanel.Resources>
<ToggleButton Name="alwaysOnTop" Checked="alwaysOnTop_Checked"
Style="{StaticResource ShortcutToggleStyle}" Unchecked="alwaysOnTop_Unchecked"
Visibility="{Binding Source=main:Properties.Settings.Default, Path=pinShow, Converter={StaticResource boolToVis}, Mode=TwoWay}" >
<Image SnapsToDevicePixels="True" Source="Images/pushpin.png"
ToolTip="Always on Top" />
</ToggleButton>
Settings Window XAML
<CheckBox Name="pinDisable" Grid.Column="0" Grid.Row="1" HorizontalAlignment="Center"
IsChecked="{Binding Source={x:Static main:Properties.Settings.Default}, Path=pinShow, Mode=TwoWay}"
Checked="pinDisable_Checked" Unchecked="pinDisable_Unchecked"/>
Convertere Code-Behind
public class BooleanToHiddenVisibility : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Visibility rv = Visibility.Visible;
try
{
var x = bool.Parse(value.ToString());
if (x)
{
rv = Visibility.Visible;
}
else
{
rv = Visibility.Collapsed;
}
}
catch (Exception)
{
}
return rv;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
}
Related
<Button
VerticalAlignment="Stretch"
ToolTip="Convert selected Node(s)"
ToolTipService.ShowOnDisabled="False"
Visibility="{Binding Source={StaticResource serviceLocator}, Path=NetworkManager.NetworkViewModel.IsGenericProfileLoaded}"
Command="{Binding Path=ConvertToNonControlPlaneCommand}" >
<Image
Style="{StaticResource toolbarImageStyle}"
Source="/Resources/Icons/equipment_edit.png"
/>
</Button>
The button should be collapsed or invisible depending on the Path property called IsGenericProfileLoaded. Even if IsGenericProfileLoaded is false, the button is still visible. How to make it invisible. From other question on stackoverflow, it seems that i need to visibilitytoBoolean converter or is there anything that I can use here like disable="true"?
In general when a data binding does not work, debug the app in Visual Studio and look in the debug output. You can show the debug output by: menu / DEBUG / Windows / Output. Go in the application to that page and keep an eye to the messages shown in the debug output window. You will get some information about what does not work.
You are correct, in your case you need a converter. The data binding alone will not be able to assign a bool to a dependency property of type Visibility.
Here is a very simple version of such convertor:
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool booleanValue = (value as bool?).GetValueOrDefault();
return booleanValue ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
You will have to also add somewhere in XAML a static reference that makes this convertor available. Maybe in App.xaml something like this:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:<replace_with_your_namespace>;assembly=<replace_with_your_assembly>">
...
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
...
</ResourceDictionary>
In your case the visibility will have to be set like this:
Visibility="{Binding Source={StaticResource serviceLocator}, Path=NetworkManager.NetworkViewModel.IsGenericProfileLoaded, Converter={StaticResource BoolToVisibilityConverter}}"
You will soon see that you need more flexibility from the convertor. You will want to convert a true to Visibility.Collapsed and a false to Visibility.Visible. Here is how such a more flexible convertor would be used in XAML:
Visibility="{Binding IsLocked, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter=not}"
And here is how the convertor method would have to be rewritten:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool booleanValue = (value as bool?).GetValueOrDefault();
if (parameter != null)
{
if (parameter.ToString().Equals("not", StringComparison.OrdinalIgnoreCase))
{
booleanValue = !booleanValue;
}
else
{
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Invalid value for the BoolToVisibilityConverter parameter: '{0}'. The only valid values are null or 'not' (case insensitive)", parameter));
}
}
return booleanValue ? Visibility.Visible : Visibility.Collapsed;
}
i am newbie in C# and Windows app development, just for learning purpose i am trying to build a Windows 10 universal app. I am experimenting with Hub view.
Below is the Xaml structure of my file.
<Hub>
<HubSection1>
//SomeData here
</HubSection1>
<HubSection2>
<DataTemplate>
<Grid>
<ListView1>
<CheckBox1>
<ListView2>
//SomeData here
<CheckBox2>
<ListView3>
//SomeData here
<CheckBox3>
<ListView4>
//SomeData here
</ListView1>
</Grid>
</DataTemplate>
</HubSection2>
<HubSection3>
//SomeData here
</HubSection3>
<HubSection4>
//SomeData here
</HubSection4>
</Hub>
So what i am trying to do is to toggle the visibility of ListView(2,3,4) Using Checkboxes(1,2,3) respectively. But in my c# sharp code i am unable to access the variables defined in my XAML file, i tried FindName() in checkbox listeners Method but it didn't helped. is there any way i can fetch data or variables or bind them ??
Use converter concept:
public class BooleanToVisibility : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool isChecked = false;
if (bool.TryParse(value.ToString(), out isChecked))
{
return isChecked ? Visibility.Visible : Visibility.Collapsed;
}
return visibility;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
XAML:
<Window x:Class="MyApp.Windows.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:MyApp.Converters">
<StackPanel>
<StackPanel.Resources>
<converters:BooleanToVisibility x:Key="boolToVisibility"/>
</StackPanel.Resources>
<CheckBox Content="Check to see ListView" Name="changeVisibility"/>
<ListView Visibility="{Binding Path=IsChecked, ElementName=changeVisibility, Converter={StaticResource boolToVisibility}}"/>
</StackPanel>
</Window>
How can I bind an inverted value of a bool in my xaml?
I know, I know, there are a lot of q&a about this, but there aren't any about a specific case: when the converter is in a view model and the radio button in a user control.
how can I correctly refer to the main window view model data context? In other case I used the code I've posted, but in this case I don't get how to use it.
UPDATE CODE:
namespace ***.ViewModel
{
[ValueConversion(typeof(bool), typeof(bool))]
public class InverseBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (targetType != typeof(bool))
throw new InvalidOperationException("The target must be a boolean");
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
class MainWindowViewModel : MyClass.UtilityClasses.ViewModelBase
{
public MainWindowViewModel(){
SaveCommand = new DelegateCommand(SaveData, CanSave );
UndoCommand = new DelegateCommand(UndoActions, CanSave);
[...]
}
....
}
ANOTHER CODE UPDATE:
In my xaml, I have a devexpress GridControl where I list my observableCollection from db. When I click one element, a layout group is populated with the relative data.
In this layout group, I have:
<StackPanel Margin="0" Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left">
<RadioButton Content="{DynamicResource Locale}" Margin="10,0,0,0" x:Name="rd_LOCALE" VerticalAlignment="Center" IsChecked="{Binding Path=REMOTO, Converter={StaticResource InverseBooleanConverter}}" GroupName="Location" Panel.ZIndex="9" TabIndex="10" />
<RadioButton Content="{DynamicResource Remoto}" Margin="10,0,6,0" x:Name="rd_REMOTO" VerticalAlignment="Center" IsChecked="{Binding REMOTO}" GroupName="Location" Panel.ZIndex="10" TabIndex="11" Tag="PRISMA" />
</StackPanel>
WhenI change from one record to another, the converter gives me error... why? It fails the "if (targetType != typeof(bool))" row. So I tried to change that like:
if (value.getType() != typeof(bool))
but in this way, without fails nothing in the code, it put the wrong value in the radiobutton!
You need to make sure that the value converter is referenced in the XAML
<UserControl.Resources>
<myproject:InverseBooleanConverter x:Key="InverseBooleanConverter" />
</UserControl.Resources>
where my project is the namespace in which the value converter class is defined
I'm using the following control template in two windows that are opened at the same time and both using the SAME viewmodel.
Here is the template;
<ControlTemplate x:Key="SecurityTypeSelectionTemplate">
<StackPanel>
<RadioButton GroupName ="SecurityType" Content="Equity"
IsChecked="{Binding Path=SecurityType, Mode=TwoWay, Converter={StaticResource EnumBoolConverter}, ConverterParameter=Equity}" />
<RadioButton GroupName ="SecurityType" Content="Fixed Income"
IsChecked="{Binding Path=SecurityType, Mode=TwoWay, Converter={StaticResource EnumBoolConverter}, ConverterParameter=FixedIncome}" />
<RadioButton GroupName ="SecurityType" Content="Futures"
IsChecked="{Binding Path=SecurityType, Mode=TwoWay, Converter={StaticResource EnumBoolConverter}, ConverterParameter=Futures}" />
</StackPanel>
</ControlTemplate>
Here is the viewmodel property:
private SecurityTypeEnum _securityType;
public SecurityTypeEnum SecurityType
{
get { return _securityType; }
set
{
_securityType = value; RaisePropertyChanged("SecurityType");
}
}
Here's the Enum:
public enum SecurityType { Equity, FixedIncome, Futures }
Here is the converter:
public class EnumToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object enumTarget, CultureInfo culture)
{
string enumTargetStr = enumTarget as string;
if (string.IsNullOrEmpty(enumTargetStr))
return DependencyProperty.UnsetValue;
if (Enum.IsDefined(value.GetType(), value) == false)
return DependencyProperty.UnsetValue;
object expectedEnum = Enum.Parse(value.GetType(), enumTargetStr);
return expectedEnum.Equals(value);
}
public object ConvertBack(object value, Type targetType, object enumTarget, CultureInfo culture)
{
string expectedEnumStr = enumTarget as string;
if (expectedEnumStr == null)
return DependencyProperty.UnsetValue;
return Enum.Parse(targetType, expectedEnumStr);
}
}
The problem is a bit strange. I have two windows that are showing slightly different views of the SAME ViewModel. The same template shown above is reused in both views.
If Equity is initially set as SecurityType, i can change this to FixedIncome by clicking on the relevant radio button. I can not then change it back to Equity.
I can however set it to Futures. But then after that, i can not change it to either FixedIncome or Equity by clicking the relevant radio buttons.
What's happening in the cases where i can not set change it back is that the Setter is called twice. the first time it's setting the value to the correct selected value, but the moment RaisePropertyChanged is fired,
the setter is invoked again, this time with the original value.
It feels like when RaisePropertyChanged, the the setter is being called by the binding from the 2nd window, thus overwriting the value being set in the first window where the user makes the selection.
Does anyone know if this is the case and how to avoid in this scenario?
Here's my version of EnumToBoolConverter:
public class EnumToBoolConverter : BaseConverterMarkupExtension<object, bool>
{
public override bool Convert(object value, Type targetType, object parameter)
{
if (value == null)
return false;
return value.Equals(Enum.Parse(value.GetType(), (string)parameter, true));
}
public override object ConvertBack(bool value, Type targetType, object parameter)
{
return value.Equals(false) ? DependencyProperty.UnsetValue : parameter;
}
}
The default behavior for a RadioButton is to update the source when the property changes so both windows are trying to update the source. One fix is to only update the source only from where the user clicked. To do this use Binding.UpdateSourceTrigger Explict on the binding. Add a click handler in code behind for RadioButton. In it explicity update the source.
<StackPanel>
<RadioButton GroupName ="SecurityType" Content="Equity"
IsChecked="{Binding Path=SecurityType, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, UpdateSourceTrigger=Explicit, ConverterParameter=Equity}" Click="RadioButton_Click" />
<RadioButton GroupName ="SecurityType" Content="Fixed Income"
IsChecked="{Binding Path=SecurityType, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, UpdateSourceTrigger=Explicit, ConverterParameter=FixedIncome}" Click="RadioButton_Click"/>
<RadioButton GroupName ="SecurityType" Content="Futures"
IsChecked="{Binding Path=SecurityType, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, UpdateSourceTrigger=Explicit, ConverterParameter=Futures}" Click="RadioButton_Click"/>
</StackPanel>
private void RadioButton_Click(object sender, RoutedEventArgs e)
{
BindingExpression be = ((RadioButton)sender).GetBindingExpression(RadioButton.IsCheckedProperty);
be.UpdateSource();
}
You may have to use a UserControl instead of or inside your ControlTemplate to get code behind in your view.
I have a play button in a AudioRecord View.
Currently it is declered as:
<Button Width="72" Height="72" Style="{StaticResource RoundPlay}"
DataContext="{Binding ElementName=this, Path=DataContext}"
cmd:ButtonBaseExtensions.Command="{Binding PlayStopCommand}"
/>
When a user clicks the button, a PlayStopCommand in items ViewModel gets executed. I want the button to get its' style set to "RoundStop" whenever the sound is playing.
How can I bind the buttons' Style to a property in my ViewModel (what property type should I use), so that the look of the button is controllable from code?
I have RoundStop style defined, I just need a way to apply it to a button from code.
You should define the playing state in you viewmodel (Playing/Stopped), and bind Button.Style to that property using a converter. In your converter, return a different style (taken from App.Current.Resources) based on the current state.
Edit:
Here's an example of your converter should look like:
public class StateStyleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (PlaybackState)value == PlaybackState.Playing ? App.Current.Resources["RoundPlay"] : App.Current.Resources["RoundStop"];
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
In this example, PlaybackState is an enum:
public enum PlaybackState
{
Playing,
Stopped
}
Then you should add the state property to your view model (The part where you notify the change depends on the framework you are using for MVVM):
private PlaybackState state;
public PlaybackState State
{
get { return state; }
set
{
state = value;
RaiseNotifyPropertyChanged("State");
}
}
Declare your converter in XAML:
<UserControl.Resources>
<converters:StateStyleConverter x:Key="StateStyleConverter"/>
</UserControl.Resources>
And finally bind it to the button:
<Button Width="72" Height="72" Style="{Binding State, Converter={StaticResource StateStyleConverter}}"
DataContext="{Binding ElementName=this, Path=DataContext}"
cmd:ButtonBaseExtensions.Command="{Binding PlayStopCommand}"
/>
You could use a ToggleButton and make the necessary visual changes in the visual states for checked/unchecked.
If you must do it the way your question states, then you can define the Style in the resources and then access it in the code-behind from this.Resources["YourStyleKey"]; Your problem will be getting it from the view to the view model, hence my first suggestion :)