Set TextBlock Visibility based on a bound bool - c#

I am trying to link the visibility of a TextBlock to a bool property which is also linked to a checkbox using WPF and c#. I have the following code in two different sections of the same xaml file (one section is a summary, and the other is settings. I am very new to WPF, and am learning as I go. Currently, the TextBlock is visible no matter what the value of IsSecondaryMessageFilePath is.
<TextBlock Name="secondaryfolderinfo" Foreground="Red">
<ContentControl Content="Secondary message folder" Foreground ="Black" />
<ContentControl Content = "{Binding Path=SecondaryMessageFilePath}" ContentStringFormat="" ClipToBounds="False"></ContentControl>
<ContentControl Content = " "></ContentControl>
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsSecondaryMessageFilePath}" Value="True">
<Setter Property="Visibility" Value="Visible"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Further down I have:
<CheckBox IsChecked="{Binding Path=IsSecondaryMessageFilePath, Mode=TwoWay}"
Name="SecondaryPathCheckBox"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Margin="320,7,0,0">Save additional locations</CheckBox>
Finally, in the code-behind, I have:
public bool IsSecondaryMessageFilePath
{
get { return _isSecondaryMessageFilePath; }
set
{
if (_isSecondaryMessageFilePath != value)
{
_isSecondaryMessageFilePath = value;
OnPropertyChanged("IsSecondaryMessageFilePath");
}
}
}
private bool _isSecondaryMessageFilePath;
public string SecondaryMessageFilePath
{
get { return _secondaryMessageFilePath; }
set
{
if (_secondaryMessageFilePath != value)
{
_secondaryMessageFilePath = value;
OnPropertyChanged("SecondaryMessageFilePath");
}
}
}
private string _secondaryMessageFilePath;
Any assistance would be appreciated.
EDIT
Working from the suggestion below, I tried adding the BooleanToVisibilityConverter, but am getting a missing assembly reference for it, and am to new to WPF to figure out how to resolve it. My opening code is as follows:
<UserControl x:Class="Sender_Receiver.SenderReceiverSetup"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:diagnostics="clr-namespace:System.Diagnostics;assembly=WindowsBase"
xmlns:m=...
xmlns:
<UserControl.Resources>
<BooleanToVisibiltyConverter x:Key="BooleanToVisibilityConverter"/>
...

Your code looks ok at first glance, but you really don't need to use a data trigger for this. WPF comes with a BooleanToVisibilityConverter class that you declare in your resources:
<BooleanToVisibiltyConverter x:Key="BooleanToVisibilityConverter"/>
Then in your TextBlock, you bind Visibility:
<TextBlock Visibility="{Binding Path=IsSecondaryMessageFilePath, Converter={StaticResource BooleanToVisibilityConverter}}"/>
Just so you know, there may be a simpler way to do this, just bind to the IsChecked property itself!
<CheckBox x:Name="UseSecondaryPath"/>
<TextBlock Visibility="{Binding ElementName=UseSecondaryPath, Path=IsChecked, Converter={StaticResource BooleanToVisibilityConverter}}"/>
Of course if you need the bool for something else that wouldn't be an ideal solution, but it is a little cleaner if its just for the UI.
The code for a custom BooleanToVisibilityConverter, if you are interested, is:
public class BooleanToVisibilityConverter : IValueConverter
{
public object Convert (object value, ...)
{
if ((bool)value)
return Visibility.Visible;
else
return Visibility.Collapsed;
}
public object ConvertBack(object value, ...)
{
return Binding.DoNothing;
}
}
Let me know if I can clarify anything or assist further.

private Boolean _IsChecked;
//Bind this to your checkbox
public Boolean IsChecked
{
get { return _IsChecked; }
set { _IsChecked= value; OnPropertyChanged("IsChecked"); OnPropertyChanged("TextBoxVis"); }
}
//Bind this to your TextBox's Visibility Property
public Visibility TextBoxVis
{
get { return IsChecked ? Visibility.Visible : Visibility.Collapsed; }
}

Related

ListBox binding to ObservableCollection<string>

In the following class, the itemssource of a listbox should bind to the Interfaces property.
public class BaseClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private const string TYPE_TITLE = "Type";
private string _Type;
public string Type
{
get { return _Type; }
set { _Type = value; this.NotifyPropertyChanged(PropertyChanged, TYPE_TITLE); }
}
public ObservableCollection<string> Interfaces { get; set; }
public BaseClass()
{
Interfaces = new ObservableCollection<string>();
}
public void Reset()
{
_Type = null;
Interfaces.Clear();
}
}
In that list box the selected item should be able to edit as the inline edit scenario,
<DataTemplate x:Key="BaseClass_Interfaces_InlineEdit_Template">
<TextBox Text="{Binding Mode=TwoWay, Path=., NotifyOnTargetUpdated=True, UpdateSourceTrigger=PropertyChanged}" TextChanged="TextBox_TextChanged"/>
</DataTemplate>
<DataTemplate x:Key="BaseClass_Interfaces_InlineView_Template">
<TextBlock Text="{Binding Path=., UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
<Style TargetType="{x:Type ListBoxItem}" x:Key="BaseClass_Iterfaces_ItemStyle_Template">
<Setter Property="ContentTemplate" Value="{StaticResource BaseClass_Interfaces_InlineView_Template}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource BaseClass_Interfaces_InlineEdit_Template}" />
</Trigger>
</Style.Triggers>
</Style>
The ListBox has a container as a parent hierarchy which its DataContext property bind to an instance of BaseClass hence the ListBox could bind to the Interfaces property.
<ListBox Grid.Row="2" Grid.ColumnSpan="2" Margin="3" ItemsSource="{Binding Interfaces, Mode=TwoWay}" SelectionMode="Single"
ItemContainerStyle="{StaticResource ResourceKey=BaseClass_Iterfaces_ItemStyle_Template}" />
The list box before select any item
Editing the selected item
Another item select after edit and the changes doesn't affected
There are two problems :
The TextBox should have "Path=." otherwise the "Two-way binding requires Path or XPath." exception message received.
With consider the above problem, the ObservableCollection items never updated after text changed!!!!!!
I found the answer!
My wondering was about the text box which the text property changed but the changes does not propagated to the source, based on the link the binding mechanism works on the properties of sources in the other words the change of the properties monitors not the object itself.
The solution is a wrapper class around the string, i wrote this wrapper before for another primitive type (bool).
public class Wrapper<T>
{
public T Item { get; set; }
public Wrapper(T value = default(T))
{
Item = value;
}
public static implicit operator Wrapper<T>(T item)
{
return new Wrapper<T>(item);
}
public static implicit operator T(Wrapper<T> item)
{
if (null != item)
return item.Item;
return default(T);
}
}
So the editing data template change as follow
<DataTemplate x:Key="BaseClass_Interfaces_InlineEdit_Template">
<TextBox Text="{Binding Mode=TwoWay, Path=Item, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
And every thing work as charm!!!

WPF Changing Image from DataTrigger

Firstly, it's worth mentioning that I've looked at other similar topics, and they've helped me get this far but I need a little help getting over the finishing line.
The problem I'm having is that I can't get my DataTrigger to show the correct image, When the InPossesion bool flag is set to false I'm setting my enum property as IconImage2, which in turn should change the image in the datagrid to a red circle, this doesn't happen. If anyone could give me any pointers as to where I'm going wrong that would be great.
ViewModel Enum
public enum IconEnum
{
IconImage1,
IconImage2
}
public IconEnum MyIconEnumProperty
{
get { return _myEnum; }
set
{
_myEnum = value;
RaisePropertyChanged("MyIconEnumProperty");
}
}
ViewModel Method to load orders
private void LoadCloakroomOrders()
{
CloakroomOrderRepository repo = new CloakroomOrderRepository();
//Get All orders
var orders = repo.GetPublic();
foreach (var orderItem in orders)
{
Orders.Add(orderItem);
if (orderItem.InPossesion == false)
{
MyIconEnumProperty = IconEnum.IconImage2;
}
}
}
XAML
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Orders}"
SelectedItem="{Binding Path=SelectedCloakroomOrder}"
Margin="0,23,0,-0.5" Width="980" >
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Visibility="{Binding ShowIcon,
Converter={StaticResource BooleanToVisibilityConverter},
FallbackValue=hidden}" >
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source" Value="/Resources/Images/circle_green.png"/>
<Style.Triggers>
<DataTrigger Binding="{Binding MyIconEnumProperty}" Value="IconImage2">
<Setter Property="Source" Value="/Resources/Images/circle_red.png"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Thanks!
Rather than having an enum in the VM, you could simply have an InPossesion property, and hide/show two images based on that. This keeps the view model cleaner, and the XAML clearer:
ViewModel:
public bool InPossession
{
get { return _inPossession; }
set { _inPossion = value; RaisePropertyChanged("InPossession"); }
}
private void LoadCloakroomOrders()
{
CloakroomOrderRepository repo = new CloakroomOrderRepository();
//Get All orders
var orders = repo.GetPublic();
foreach (var orderItem in orders)
{
Orders.Add(orderItem);
if (orderItem.InPossesion == false)
{
InPossession = false;
}
}
}
Converter:
public class BooleanToVisibilityConverter : IValueConverter
{
public Visibility VisibilitIfTrue { get;set; }
public Visibility VisibilitIfFalse { get;set; }
public BooleanToVisibilityConverter()
{
// Set default values for the most common usage
VisibilityIfTrue = Visible;
VisibilityIfFalse = Collapsed;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// Converter could be extended to handle nullable bools as well, but ignore for now
if (value == null)
{
return DependencyProperty.UnsetValue;
}
// value should be of type bool
bool b = (bool)value;
if (b == true)
{
return VisibilityIfTrue;
}
else
{
return VisibilityIfFalse;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException(); // Not necessary
}
}
XAML:
<UserControl>
<UserControl.Resources>
<converters:BooleanToVisibilityConverter x:Key="TrueToVisibleConverter" VisibilityIfTrue="Visible" VisibleIfFalse="Collapsed"/>
<converters:BooleanToVisibilityConverter x:Key="FalseToVisibleConverter" VisibilityIfTrue="Collapsed" VisibleIfFalse="Visible"/>
</UserControl.Resources>
</UserControl>
...
<DataTemplate>
<Grid Visibility="{Binding ShowIcon, FallbackValue=hidden}">
<Image Source="/Resources/Images/circle_green.png" Visibility="{Binding InPossession, Converter={StaticResource TrueToVisibleConverter}}"/>
<Image Source="/Resources/Images/circle_red.png" Visibility="{Binding InPossession, Converter={StaticResource FalseToVisibleConverter}}"/>
</Grid>
</DataTemplate>
Try also specify the enum type.
Value="{x:Static wpf:IconEnum.IconImage2}"
wpf: is a namespace like xmlns:wpf="clr-namespace:Sandbox.WPF" in my case. But I would probably go for another solution, like sondergard suggests, it's much cleaner style than this hacking.

Bind a property in xaml template to viewmodel (RibbonTextBox)

Background info of larger problem
The problem I am trying to solve is to allow a user to set the MinWidth of the label inside of the RibbonTextBox control template. I intend to the same with other properties once I can figure out the first one. The aim of this is to be able to align RibbonTextBoxes stacked on top of each other by setting widths. I am so far solved my problem by hardcoding the values in the control template. I would like to make this control reusable and thus need to be able to set up some binding.
The problem that needs solving
I have the following xaml (lots of xaml has been removed for readability). At the centre of this xaml you can see a label. That label has a MinWidth property which is the focus of my question.
<DataTemplate x:Uid="DataTemplate_0" DataType="{x:Type element:RibbonTextBoxVM}">
<ribbon:RibbonTextBox x:Uid="ribbon:RibbonTextBox_1" IsReadOnly="{Binding IsReadOnly}" Text="{Binding Text}" Label="{Binding Label}" >
<ribbon:RibbonTextBox.Style>
<Style TargetType="{x:Type ribbon:RibbonTextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ribbon:RibbonTextBox}">
<StackPanel Orientation="Horizontal">
<Label Margin='2,0,0,0' Padding='0,0,0,5' BorderThickness='0,0,0,0' HorizontalAlignment='Stretch' VerticalAlignment='Bottom'
HorizontalContentAlignment='Left' VerticalContentAlignment='Top' Background='#00FFFFFF' FlowDirection='LeftToRight'
Visibility='Visible' MinWidth="80">
<!--other stuff-->
</Label>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ribbon:RibbonTextBox.Style>
</ribbon:RibbonTextBox>
</DataTemplate>
The following is the viewmodel that backs the above xaml.
public class RibbonTextBoxVM : ViewModel
{
public string Label
{
get { return GetValue(Properties.Label); }
set { SetValue(Properties.Label, value); }
}
public string Text
{
get { return GetValue(Properties.Text); }
set { SetValue(Properties.Text, value); }
}
public bool IsReadOnly
{
get { return GetValue(Properties.IsReadOnly); }
set { SetValue(Properties.IsReadOnly, value); }
}
public RibbonTextBoxVM(string text, string label, bool isReadOnly)
{
Text = text;
Label = label;
IsReadOnly = isReadOnly;
}
}
What I would like to do is have a property LabelMinWidth.
public double LabelMinWidth
{
get { return GetValue(Properties.LabelMinWidth); }
set { SetValue(Properties.LabelMinWidth, value); }
}
I want to allow the user to pass in a value to the constructor to set that property. That is the easy part.
The part I cannot figure out is how to bind my new LabelMinWidth to the MinWidth property of the label inside the control template in the xaml.
If anyone can point me in the right direction that would be great. Ill be happy to answer any questions about the problem.
Since your In your RibbonTextBox has your VM as its DataContext, you can use a Bindingin your ControlTemplate, just like you bound the other properties:
<Label ... MinWidth="{Binding LabelMinWidth}">
This works because in WPF, the DataContext inherits to all children (unless overridden). So if you have a property on your VM that you want to bind to in a control in a template, you just bind to it.

Setting a treeview items text colour in realtime based on a ViewModel property

In my WPF application I am using the MVVM pattern. My view has a treeview which I bind an observableCollection of objects as defined below. What I want to do is to change the colour of a tree item name when the bound object sets it’s dirty property to true. I can get it to set the colour when I first populate the tree but then it doesn’t reflect the changes when the property changes between false and true.
public class HierarchicalItem
{
private readonly ObservableCollection<HierarchicalItem> _children = new ObservableCollection<HierarchicalItem>();
public ViewModelBase ViewModel { get; set; }
public string Name
{
get { return ViewModel.ViewModelName; }
}
public ICollection<HierarchicalItem> Children
{
get { return _children; }
}
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
if (_isSelected)
EventSystem.Publish(new SelectedViewModelMessage { SelectedViewModel = ViewModel });
}
}
public bool IsDirty
{
get { return ViewModel.IsDirty; }
}
}
This is the treeview xaml:
<TreeView Grid.Row="0" Grid.Column="0" ItemsSource="{Binding Path=Views}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True"/>
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=OneWayToSource}" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:HierarchicalItem}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IsDirty}" Value="True">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
Here is the collection that gets bound to the tree:
private readonly ObservableCollection<HierarchicalItem> _views = new ObservableCollection<HierarchicalItem>();
public ObservableCollection<HierarchicalItem> Views
{
get { return _views; }
}
The ViewModels that are referenced in the HierarchicalItem collection all derive from a base class that exposes the “IsDirty” property. This is definantly changing state so I’m not sure if I’ve made a coding mistake or if what I want to achieve can’t be done this way. The classes all use the “INotifyPropertyChanged” interface. Here is the “IsDirty” property in from the ViewModel base class:
public class ViewModelBase : ValidatableModel
{
#region Properties
private bool _isDirty;
public bool IsDirty
{
get { return _isDirty; }
protected set
{
_isDirty = value;
OnPropertyChanged("IsDirty");
}
}
.
.
.
Etc
It's because your HierarchicalItem (the one you are having issues with) does not use a full INPC approach for its IsDirty property. The viewmodel does, but that is not enough, as the DataTemplate will be using the IsDirty property of the HierarchicalItem, so that needs to be full INPC property too
Changed that to this and it should be ok.
private bool _isDirty;
public bool IsDirty
{
get { return _isDirty; }
protected set
{
_isDirty = value;
OnPropertyChanged("IsDirty");
}
}
Though for your use case you will need to figure out some way to fire that. Or another thing you could try would be to change the binding in HierarchicalItem DataTemplate to this
<DataTrigger Binding="{Binding ViewModel.IsDirty}" Value="True">

How to disable a button if no items are selected in a ListView

I have a ListView Contained in a UserControl I would like to disabled a button when no items are selected in the UserControl, would it be the right way to do it? So far, it doesn't disable, it just stays enable all the way.
I've included the xaml code.
searchAccountUserControl is the UserControl name property in the xaml.
And AccountListView is the ListView name property in the userControl xaml.
<Button Content="Debit" IsEnabled="true" HorizontalAlignment="Left" Margin="18,175,0,0" Name="DebitButton" Width="128" Grid.Column="1" Height="32" VerticalAlignment="Top" Click="DebitButton_Click">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=searchAccountUserControl.AccountListView, Path=SelectedValue}" Value="{x:Null}" >
<Setter Property="Button.IsEnabled" Value="false"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Thanks.
Finally i've used :
in my ViewModel :
private bool _isSelected;
public bool IsSelected { get { return _isSelected; }
set { _isSelected = _account.View.CurrentItem != null;
PropertyChanged.SetPropertyAndRaiseEvent(this, ref _isSelected, value,
ReflectionUtility.GetPropertyName(() => IsSelected)); } }
And then Use isEnabled = "{Binding Path=IsSelected}" in the xaml.
There are a few things wrong here.
Precedence, if you set IsEnabled on the control itself the style will never be able to change it.
ElementName, it's an ElementName, not a path, just one string that gives the name of one element. Everything beyond that goes into the Path.
Style syntax, if you set a Style.TargetType you should not set the Setter.Property with a type prefix (although leaving it does not break the setter).
By the way, this alone is enough:
<Button IsEnabled="{Binding SelectedItems.Count, ElementName=lv}" ...
It's obvious that you aren't using Commanding (ICommand Interface). You should either use that (and preferably the Model-View-ViewModel architecture).
But, if you want to stick with code-behind and XAML:
<ListView SelectionChanged="AccountListView_SelectionChanged" ... />
private void AccountListView_SelectionChanged(Object sender, SelectionChangedEventArgs args)
{
DebitButton.IsEnabled = (sender != null);
//etc ...
}
More information on MVVM: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
You need to set the DataContext of the View (UserControl) to the instance of the ViewModel you want to use. Then, from there, you can bind to properties on the ViewModel, including ICommands. You can either use RelayCommand (see link above) or use Commanding provided by a framework (for example, Prism provides a DelegateCommand). These commands take an Action (Execute) and a Func (CanExecute). Simply provide the logic in your CanExecute. Of course, you'd also have to have your ListView SelectedItem (or SelectedValue) be databound to a property on your ViewModel so you can check to see if it's null within your CanExecute function.
Assuming you use RelayCommand you don't have to explicitly call the RaiseCanExecuteChanged of an ICommand.
public class MyViewModel : ViewModelBase //Implements INotifyPropertyChanged
{
public MyViewModel()
{
DoSomethingCommand = new RelayCommand(DoSomething, CanDoSomething);
}
public ObservableCollection<Object> MyItems { get; set; }
public Object SelectedItem { get; set; }
public RelayCommand DoSomethingCommand { get; set; }
public void DoSomething() { }
public Boolean CanDoSomething() { return (SelectedItem != null); }
}
<ListView ItemsSource="{Binding MyItems}" SelectedItem="{Binding SelectedItem}" ... />
<Button Command="{Binding DoSomethingCommand}" ... />

Categories

Resources