Hid parts of user control based on data source in wpf - c#

I have a user control which contains 2 shapes, an ellipse and rectangle. What I want to do is hide the rectangle if the data source I'm binding to is set to false, and visible if true. The same for the ellipse.
So in my case how can I set the visibility of the ellipse based on the ismale bool. Also set the rectangle visibility based on isFemale bool?
User Control
<UserControl>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center"
MinWidth="200" MinHeight="120">
<Rectangle Fill="Gray" Margin="5" />
<Ellipse Fill="Red" Margin="5" />
</Grid>
</UserControl>
Pseudo code for class object I'm data binding to in my mvvm setup.
class Person
{
public bool IsMale {get;set;}
public bool IsFemale {get;set;}
public string Name {get;set;}
}

You can simply use DataTrigger for each Style of the shape.
<UserControl>
<Grid HorizontalAlignment="Center"
VerticalAlignment="Center"
MinWidth="200"
MinHeight="120">
<Rectangle Fill="Gray" Margin="5">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Visibility" Value="Collpased"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsFemale}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
<Ellipse Fill="Red" Margin="5">
<Ellipse.Style>
<Style TargetType="Ellipse">
<Setter Property="Visibility" Value="Collpased"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsMale}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
</Grid>
</UserControl>
Also, note that the DataContext of the UserControl must of of type Person object.

Related

Using style triggers on a static resource style to change more than properties

I am using Material Design XAML and I am trying to set a togglebutton icon and background color to something else when checked.
RED BOX CODE
<ToggleButton ToolTip="MaterialDesignFlatPrimaryToggleButton"
IsChecked="False"
Grid.Column="2"
Margin="78,58,99,52"
Grid.Row="1">
<Style TargetType="ToggleButton"
BasedOn="{StaticResource MaterialDesignFlatPrimaryToggleButton}">
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="Green" />
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton>
GREEN BOX CODE
<ToggleButton Style="{StaticResource MaterialDesignFlatPrimaryToggleButton}"
ToolTip="MaterialDesignFlatPrimaryToggleButton"
IsChecked="False"
Grid.ColumnSpan="2"
Grid.Column="3"
Margin="205,58,188,52"
Grid.Row="1">
<md:PackIcon Kind="WindowClose"
Foreground="Red"
Height="21"
Width="21"/>
</ToggleButton>
I was thinking I could use the basedon attribute to reserve the style with material design...if possible.
I am trying reserve the style and change the icon and set background color when checked to look like this:
How would I do this?
You are currently setting the Style as the ToggleButton.Content.
You've got to define the Style nested into the ToggleButton.Style property:
<ToggleButton ToolTip="MaterialDesignFlatPrimaryToggleButton"
IsChecked="False"
Grid.Column="2"
Margin="78,58,99,52"
Grid.Row="1">
<ToggleButton.Style>
<Style TargetType="ToggleButton"
BasedOn="{StaticResource MaterialDesignFlatPrimaryToggleButton}">
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="Green" />
<Setter Property="Content">
<Setter.Value>
<md:PackIcon Kind="SmileyHappy" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>

How do I change a controls background on my UserControl when the item is selected

So I have this ListView which has a DataTemplate of my UserContol because I wanted a custom design for my ListView and it looks like this
<ListView x:Name="LeftMenuListView"
ItemsSource="{Binding MenuItems}"
SelectedItem="{Binding SelectedMenuItem}"
BorderThickness="0"
Width="255">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<local:MenuItemControl/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Super simple, now when an Item is selected the entire thing changes color
which I want it looks great imo
<Style TargetType="ListViewItem">
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border
Name="Border"
BorderThickness="0">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="Border" Property="Background"
Value="#444444"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
But there is a border inside my usercontrol thats 10px wide with the name SmallBorder.
I want to change the color of that to green when the item is selected but I have no idea how to access that property
My UserControl
<Grid Background="Transparent">
<TextBlock Text="{Binding Name}"
VerticalAlignment="Center"
Margin="20,0,0,0"
Foreground="#9e9e9e"
FontFamily="Tahoma"/>
<Border Width="10"
HorizontalAlignment="Left"
x:Name="SmallBorder"/>
</Grid>
So how do I change the color of SmallBorder when an item is selected and then when it's not selected it turns transparent?
The ViewModel, which is the DataContext of you usercontrol, should expose a property like IsSelected, then you can add an style with a DataTrigger that reacts to a change in this property.
EDIT:
Declare an style for the border itself an access it as an StaticResource:
It could be placed in a ResourceDictionary, within YourUserControl.Resources or inline with the Border control declaration:
<Style TargetType={x:Type Border} x:Key=SelectedBorderStyle>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="BorderBrush" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
And then your UserControl would be:
<Grid Background="Transparent">
<TextBlock Text="{Binding Name}"
VerticalAlignment="Center"
Margin="20,0,0,0"
Foreground="#9e9e9e"
FontFamily="Tahoma"/>
<Border Width="10"
Style={StaticResource SelectedBorderStyle}
HorizontalAlignment="Left"/>
</Grid>
Note that now you don't need to set the name for the Border.
A Border is invisible unless there is something in it, but you could replace the Border with a Grid and use a Style with a DataTrigger that binds to the IsSelected property:
<Grid Background="Transparent">
<TextBlock Text="{Binding Name}"
VerticalAlignment="Center"
Margin="20,0,0,0"
Foreground="#9e9e9e"
FontFamily="Tahoma"/>
<Grid Width="10"
HorizontalAlignment="Left"
x:Name="SmallBorder">
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListViewItem}}" Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
</Grid>

MVVM WPF images on canvas

I have a code that allows you to create objects on the screen, there is a panel of buttons, clicking on the button you create an image / object on canvas, I can not get another image to be placed in Canvas so that several images can be displayed, link to a previously created question: WPF C # Display objects (2d Map)
<Thumb.Template>
<ControlTemplate TargetType="Thumb">
<Canvas>
<Image x:Name="injWell" Source="/Resources/injectionWell.png"
HorizontalAlignment="Center" VerticalAlignment="Center"
Width="40" Height="40"
Stretch="Fill" StretchDirection="Both" IsHitTestVisible="False"/>
<Image x:Name="injWellNot" Source="/Resources/injectionWellNot.png"
HorizontalAlignment="Center" VerticalAlignment="Center"
Width="40" Height="40"
Stretch="Fill" StretchDirection="Both" IsHitTestVisible="False"/>
<TextBlock Canvas.Top="-20" Canvas.Left="-40" Width="100"
TextAlignment="Center" Text="{Binding Name}" FontWeight="Bold"
IsHitTestVisible="False"
Visibility="{Binding DataContext.ShowNames,
RelativeSource={RelativeSource FindAncestor, AncestorType=Window},
Converter={StaticResource BoolToVisibilityConverter}}"/>
<TextBlock Canvas.Left="30" Canvas.Top="10"
Text="{Binding X, StringFormat='{}X = {0}'}"
IsHitTestVisible="False"
Visibility="Collapsed" x:Name="XText"/>
<TextBlock Canvas.Left="30" Canvas.Top="25"
Text="{Binding Y, StringFormat='{}Y = {0}'}"
IsHitTestVisible="False"
Visibility="Collapsed" x:Name="YText"/>
</Canvas>
<ControlTemplate.Triggers>
<Trigger Property="IsDragging" Value="True">
<Setter TargetName="injWell" Property="Source" Value="/Resources/injectionWell.png"/>
<Setter TargetName="injWellNot" Property="Source" Value="/Resources/injectionWellNot.png"/>
</Trigger>
<DataTrigger Binding="{Binding DataContext.ShowAllCoordinates, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" Value="True">
<Setter TargetName="XText" Property="Visibility" Value="Visible"/>
<Setter TargetName="YText" Property="Visibility" Value="Visible"/>
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" Value="True"/>
<Condition Binding="{Binding DataContext.ShowCurrentCoordinates, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" Value="True"/>
</MultiDataTrigger.Conditions>
<Setter TargetName="XText" Property="Visibility" Value="Visible"/>
<Setter TargetName="YText" Property="Visibility" Value="Visible"/>
</MultiDataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Thumb.Template>
</Thumb>
</DataTemplate>
The key thing to realize here is that you need to render a list of items, and in MVVM you do that with an ItemsControl. (You could also use a ListView, but they implement additional functionality that you almost certainly wont want, like item selection etc). Ultimately you want all of your graphics objects to be displayed on a Canvas, so you need to override the ItemsPanel property and put one there inside a ItemsPanelTemplate. Each element then needs to be positioned on the Canvas, so you'll need to set the ItemContainerStyle and set the Canvas.Left and Canvas.Top properties there (a common mistake is to do it on the elements themselves, but they get placed into a container so you have to do it on that instead). Put all this together and you'll have something that looks like this:
<ItemsControl ItemsSource="{Binding MyGraphicsViewModels}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="Canvas.Left" Value="{Binding X}" />
<Setter Property="Canvas.Top" Value="{Binding Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
So MyGraphicsViewModels will typical be a list in your view model of some base class you've created (e.g. BaseGraphicsViewModel) and then you'll subclass that into all the different types of graphics items you want to draw. The final piece of the puzzle is to then add DataTemplates to specify how you want each of your view model types to be represented in the view:
<DataTemplate DataType="{x:Type vm:DerivedGraphicsObjectViewModel}">
<Rectangle Fill="Blue" Width="64" Height="64" />
</DataTemplate>
For a more comprehensive example, which uses Triggers instead of DataTemplates, check out my answer to this SO question from the past.

Changing ellipse fill color inside a button

I want to change the ellipse fill color to green when the button is enabled.
<Button x:Name="btn_Kaart2" Grid.Column="1" Grid.Row="2" IsEnabled="False">
<Button.Template>
<ControlTemplate>
<Ellipse x:Name="ellipse_2"
Height="35"
Stroke="Black"
Fill="Red"
Margin="-300,440,0,0"/>
</ControlTemplate>
</Button.Template>
</Button>
Normally I would use ellipse_2.Fill = "Color" but that doesn't work, program can't find ellipse_2
You can use a databinding with RelativeSource to get this without any additional code
<Button x:Name="btn_Kaart2" Grid.Column="1" Grid.Row="2" IsEnabled="False">
<Button.Template>
<ControlTemplate>
<Ellipse x:Name="ellipse_2"
Height="35"
Margin="-300,440,0,0"
Stroke="Black">
<Ellipse.Style>
<Style TargetType="Ellipse">
<Style.Triggers>
<DataTrigger Binding="{Binding IsEnabled, RelativeSource={RelativeSource AncestorType=Button}}" Value="false">
<Setter Property="Fill" Value="red"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding IsEnabled, RelativeSource={RelativeSource AncestorType=Button}}" Value="true">
<Setter Property="Fill" Value="green"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
</ControlTemplate>
</Button.Template>
</Button>
It's important that the Ellipse itself has no Fill attribute.
You can try this by changing the IsEnabled value in the *.xaml and the color should change immediately in the designer.

XAML - refine watermark code

I am creating a log in page for my wpf application and want to add watermarks onto the username and password fields. I can achieve this by duplicating code but this is not a satisfactory solution for me. Is there a way I can pass string value through to the watermark binding seen in my Grid.resources section?
the goal is to refactor these two controls so they both can use the single resource login hint below, I have done research on this but came up short on information. Thanks
<Grid.Resources>
<VisualBrush x:Key="LoginHint" Stretch="Uniform" AlignmentX="Left" AlignmentY="Center" >
<VisualBrush.Visual>
<Grid HorizontalAlignment="Left">
<TextBlock FontFamily="Corbel"
HorizontalAlignment="Left" VerticalAlignment="Center" Foreground="Gray" FontStyle="Italic" Opacity="0.3"
Text="Enter Username"/>
</Grid>
</VisualBrush.Visual>
</VisualBrush>
</Grid.Resources>
<TextBox FontSize="24" BorderBrush="#008AB8" BorderThickness="1" Grid.Row="1" x:Name="usernameTextBox" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="25">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=usernameTextBox,Path=Text}" Value="" >
<Setter Property="Background" Value="{StaticResource LoginHint}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<PasswordBox FontSize="24" BorderBrush="#008AB8" BorderThickness="1" Grid.Row="2" x:Name="passwordMarkTextBox" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="25">
<PasswordBox.Style>
<Style TargetType="{x:Type PasswordBox}">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=passwordMarkTextBox,Path=Text}" Value="" >
<Setter Property="Background" Value="{StaticResource LoginHint}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</PasswordBox.Style>
</PasswordBox>
I would create a custom attached property (e.g., WatermarkText) and then create new control templates which bind to that value and superimpose the watermarks on top of the regular text area. Use triggers to govern its visibility, showing it only when no text or password has been entered. You may need a separate, readonly (computed) property to control its visibility in order to work with both TextBox and PasswordBox.

Categories