Conditional styling of an element in XAML - c#

I am building a Windows phone 8 app that has a view model property of:
public bool IsReply {get; set;}
In my xaml code, I would like to distinguish two cases:
IsReply=True
<Grid Margin="0,0,0,0">
...
</Grid>
IsReply=False
<Grid Margin="40,0,0,0">
...
</Grid>
Basically, I would like to style the Grid element depending on the value of IsReply. I know that in WPF Style.Triggers exists, but apparently not in WP.
The solution I have right now is to have a duplicate copy of the entire grid code and set the visibility of each to a data converter. However, I feel this should be simpler to do.

The easiest way is to use a Style with Triggers:
<Grid>
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Margin" Value="40 0 0 0"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsReply}" Value="True">
<Setter Property="Margin" Value="0 0 0 0"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>

You can bind the margin of the grid in your MVVM
<Grid Margin="{Binding margin}">
...
</Grid>
In your model
if(IsReply)
margin = new Thickness("0,0,0,0");
else
margin = new Thickness("40,0,0,0");
No need to create separate grids.

You can use DataTrigger, but you have to add these two references (right click on References in your project and AddReference/Assemblies/Extensions/ ... ).
xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<Grid
Margin="0">
<i:Interaction.Triggers>
<ei:DataTrigger
Binding="{Binding Path=IsReply}"
Value="True">
<ei:ChangePropertyAction
PropertyName="Margin"
Value="0" />
</ei:DataTrigger>
<ei:DataTrigger
Binding="{Binding Path=IsReply}"
Value="False">
<ei:ChangePropertyAction
PropertyName="Margin"
Value="40,0,0,0" />
</ei:DataTrigger>
</i:Interaction.Triggers>
</Grid>

Related

Ellipse as radio button

In my project, I have a little color picker that is in fact an ItemsControl with SolidColorBrushes as items, and an Ellipse as ItemTemplate.
I want the user to pick a color, when he clicks the Ellipse I want the BorderThickness to go from 0 to 2, in order to highlight the selected Ellipse.
I already managed to change the BorderThickness when the user hovers the item, using triggers. But where would I save the information about which color is selected? I can not really think of an approach here. And how can I manage that the trigger on hovering still fires even when the trigger for selected has already been activated?
Thanks in advance.
Here's some markup and code to consider.
The Selected item of the listbox is also made the current item of the collectionview. You can bind that, get it in code in the viewmodel and also navigate to next and previous.
https://msdn.microsoft.com/en-us/library/system.windows.data.collectionview.currentitem%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
I bind the rectangle to the current brush so when you select a different entry that changes. The listbox already has a lightblue background appears as you mouseover an item which highlights it somewhat. My trigger also increases the size of the ellipse a bit.
<Window.DataContext>
<local:MainWIndowViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListBox ItemsSource="{Binding BrushesView}"
IsSynchronizedWithCurrentItem="True"
>
<ListBox.ItemTemplate>
<DataTemplate>
<Ellipse Fill="{Binding}" Height="20" Width="20">
<Ellipse.Style>
<Style TargetType="Ellipse">
<Setter Property="Stroke" Value="Gray"/>
<Setter Property="StrokeThickness" Value="1"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={
RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}}"
Value="True">
<Setter Property="Ellipse.Stroke" Value="Black"/>
<Setter Property="Ellipse.StrokeThickness" Value="2"/>
</DataTrigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform ScaleX="1.2" ScaleY="1.2" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Rectangle
Grid.Column="1"
Width="40"
Height="40"
Fill="{Binding BrushesView/}"/>
</Grid>
The viewmodel:
public class MainWIndowViewModel
{
public CollectionView BrushesView { get; set; }
private ObservableCollection<SolidColorBrush> BrushesList { get; set; } =
new ObservableCollection<SolidColorBrush>
{
Brushes.Yellow, Brushes.Pink, Brushes.Blue, Brushes.Green, Brushes.Red, Brushes.Purple
};
public MainWIndowViewModel()
{
BrushesView = (CollectionView)new CollectionViewSource { Source = BrushesList }.View;
}
}

Metro Tab Control Style WPF

I would like to achieve a tabcontrol Style like the one In this project (https://github.com/thielj/MetroFramework). Unfortunately the metroframework project only works for winforms.
Can someone help me to get this styled in xaml?
Would help me alot already, if I have the style for the blue/gray line separating tabitem and tabcontent.
Thanks in advance.
First create two ResourceDictionary to store new templates, one for TabControl and one for TabItem and name them TabControlTemplate.xaml and TabItemTemplate.xaml
Create a copy of default templates for controls mentioned above by right clicking on TabControl and TabItem and then choosing Edit Template > Edit a copy.... Then choose your style names to MetroLikeTabControl and MetroLikeTabItem and set target resource dictionaries for each template. Visual studio creates a copy of that template in selected files.
In TabControlTemplate.xaml add this setter tag to control styles:
<Setter Property="ItemContainerStyle" Value="{DynamicResource MetroLikeTabItem}" />
Then change this part of template:
<TabPanel Grid.Row="0" Grid.Column="0" x:Name="HeaderPanel"
Margin="2,2,2,0" Panel.ZIndex="1"
Background="Transparent" IsItemsHost="True" KeyboardNavigation.TabIndex="1" />
to this new one:
<Grid Grid.Row="0" Grid.Column="0">
<Border BorderThickness="0 0 0 2" BorderBrush="Gray" />
<TabPanel x:Name="HeaderPanel" Margin="2,2,2,0" Panel.ZIndex="1"
Background="Transparent" IsItemsHost="True" KeyboardNavigation.TabIndex="1" />
</Grid>
this adds a border with only bottom thickness and makes your TabPanel's border overlap with TabItem's border (Why only bottom border? because I'm implementing what you want when TabControl's TabStripPlacement property is set to Top. You can set triggers to implement all other states.
In TabItemTemlate.xaml set a BorderBrush = "0 0 0 2" for element with name innerBorder and remove Opacity = "0" property.
Then change styles of IsMouseOver = true, IsSelected = true and IsSelected = false (default style of a TabItem) as desired. This is my edited trigger for Selected state that changes content of TabItem and color of Border to Blue.
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true" />
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type TabControl}}}" Value="Top" />
</MultiDataTrigger.Conditions>
<Setter Property="Panel.ZIndex" Value="1" />
<!-- commented line below, because we don't need Select Scale behaviour in metro style anymore -->
<!--<Setter Property="Margin" Value="-2,-2,-2,0" />-->
<Setter TargetName="innerBorder" Property="Opacity" Value="1" />
<Setter TargetName="innerBorder" Property="BorderThickness" Value="0,0,0,2" />
<Setter TargetName="innerBorder" Property="BorderBrush" Value="#0088cc" />
<Setter TargetName="contentPresenter" Property="TextBlock.Foreground" Value="#0088cc" />
<Setter TargetName="mainBorder" Property="BorderThickness" Value="0" />
</MultiDataTrigger>

WPF Change Window Layout Based on Combo Box Selection Using MVVM

I need to change the layout of my window based on what the user selects in a combo box. I've made a stab at what one way might be but feel like it is clunky and hacked together. Im certain their must be a cleaner MVVM solution.
My thoughts where to have multiple dock panels in my GroupBox Whose visibility is set to collapse. When the selection is made, the appropriate dockpanel will be set to visible. I attempted to find a way to do this inside the view model with no success. I also couldn't help but think my attempts are violating MVVM.
XAML
<GroupBox Header="Options">
<Grid>
<DockPanel LastChildFill="False" x:Name="syncWellHeadersDockPanel" Visibility="Collapsed">
<Button DockPanel.Dock="Right" Content="Test"></Button>
</DockPanel>
<DockPanel LastChildFill="False" x:Name="SyncDirectionalSurveyDockPanel" Visibility="Collapsed">
<Button DockPanel.Dock="Left" Content="Test02"></Button>
</DockPanel>
</Grid>
</GroupBox>
ViewModel - Property for Selected Item for ComboBox
private StoredActionsModel _selectedStoredAction = DefaultStoredAction.ToList<StoredActionsModel>()[0];
public StoredActionsModel SelectedStoredAction
{
get { return _selectedStoredAction; }
set
{
if (value != _selectedStoredAction)
{
// Unset Selected on old value, if there was one
if (_selectedStoredAction != null)
{
_selectedStoredAction.Selected = false;
}
_selectedStoredAction = value;
// Set Selected on new value, if there is one
if (_selectedStoredAction != null)
{
_selectedStoredAction.Selected = true;
}
OnPropertyChanged("SelectedStoredAction");
if (_selectedStoredAction.StoredActionID == 4)
{
//X:SyncWellHeaderDockPanel.visibility = true?????
}
}
}
}
Here's a pure-XAML way to do exactly what you're asking how to do. It's a bit verbose.
Notice that we no longer set Visibility in attributes on the DockPanels. If we still did that, the values set in the Style trigger would be overridden by the attributes. That's the way dependency properties work.
<GroupBox Header="Options">
<Grid>
<DockPanel LastChildFill="False" x:Name="syncWellHeadersDockPanel" >
<Button DockPanel.Dock="Right" Content="Test"></Button>
<DockPanel.Style>
<Style TargetType="DockPanel" >
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger
Binding="{Binding SelectedStoredAction.StoredActionID}"
Value="1"
>
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
</DockPanel>
<DockPanel LastChildFill="False" x:Name="SyncDirectionalSurveyDockPanel">
<Button DockPanel.Dock="Left" Content="Test02"></Button>
<DockPanel.Style>
<Style TargetType="DockPanel" >
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger
Binding="{Binding SelectedStoredAction.StoredActionID}"
Value="2"
>
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
</DockPanel>
</Grid>
</GroupBox>
Another way to do this would be to pass SelectedStoredAction.StoredActionID to a DataTemplateSelector, but that involves writing C# code that knows what your XAML resource keys are, and I'm not a fan.

Change style (in ResourceDictionary) from code

I have this ResourceDictionary
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="MainMenuLabelStyle" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property ="IsMouseOver" Value="True">
<Setter Property= "Foreground" Value="White"/>
<Setter Property= "FontSize" Value="18"/>
<Setter Property= "FontFamily" Value="Arial"/>
</Trigger>
</Style.Triggers>
</Style>
If I want change the font size or color, what can I do ? This code doesn't work .
Application.Current.Resources("MainMenuLabelStyle") = 25
This is the xaml
<TextBlock Text="Uscita" Grid.Row="1" Grid.Column="1" TextAlignment="Left" Margin="4" TextWrapping="Wrap" Style="{DynamicResource MainMenuLabelStyle}">
Just before a style is used for the first time in a WPF application, it is sealed for performance reasons and it is not possible to modify it anymore. You can read it on MSDN.
So, if you want to change your style, you have to options. The first one (the easiest one) is to declare as many styles as you need and put them in your ResourceDictionary.
The second solution is to consider that a Setter is a DependencyObject, so you can bind its dependency properties. In this case your style will become:
<Style x:Key="MainMenuLabelStyle" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Tag.Foreground, TargetNullValue=Red, FallbackValue=Red}" />
<Setter Property="FontSize" Value="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Tag.FontSize, TargetNullValue=18, FallbackValue=18}" />
<Setter Property="FontFamily" Value="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Tag.FontFamily, TargetNullValue=Arial, FallbackValue=Arial}" />
</Trigger>
</Style.Triggers>
</Style>
Now you can change the style just by setting the Tag property of every TextBlock control:
<StackPanel>
<TextBlock Text="Uscita" TextAlignment="Left" Margin="4" TextWrapping="Wrap" Style="{DynamicResource MainMenuLabelStyle}" />
<TextBlock Text="Uscita" TextAlignment="Left" Margin="4" TextWrapping="Wrap" Style="{DynamicResource MainMenuLabelStyle}">
<TextBlock.Tag>
<local:StyleConfig FontSize="50" FontFamily="Tahoma" Foreground="Orange" />
</TextBlock.Tag>
</TextBlock>
</StackPanel>
As you can see the first TextBlock will use the style as it was declared. On the other side, the second TextBlock will use a modified version of the original style.
Of course, in order to make this option work correctly, you must create a class (StyleConfig in my sample), which could be something like this:
public class StyleConfig
{
public string Foreground { get; set; }
public string FontSize { get; set; }
public string FontFamily { get; set; }
}
I hope it can help you.
In your code:
Application.Current.Resources("MainMenuLabelStyle") = 25
1) Wrong syntax. Application.Current.Resources["MainMenuLabelStyle"]
2) Application.Current.Resources["MainMenuLabelStyle"] this code will return object with type Style, not style property Font Size.
You can create new Style and replace it in ResourceDictionary.

Passing View as Command Parameter in WPF.Does it violet MVVM approach?

I have data grid and there some number of columns in it.There are number of rows. I want to show one window when user clicks on the context menu of that row.I need first columns value in viewmodel from that row for some logic.Currently I am passing placement target as command parameter i.e.gridviewrow. Following is my code
<telerik:RadGridView.RowStyle>
<Style TargetType="telerik:GridViewRow">
<Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" ></Setter>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="show Window" Command="{Binding PlacementTarget.Tag.DataContext.ShowChart,RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}" CommandParameter="{Binding PlacementTarget, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"></MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</telerik:RadGridView.RowStyle>
How can I pass value of first column of particular row on which user has clicked?
Does it violet MVVM approach?What is solution if it violets MVVM approach in this case?
Teoretically yes, because the only way to access the view is by INotifyPropertyChanged and IErrorDataInfo. However, it depends what do you inttend to do. If you want to change the visibilty of a UI elemnt, I violet the MVVM pattern because the other way arround it seems to complicated to me. I suggest to tell what exactly do you want to do, and maybe I will be able to help you :)
Maybe You can try to create a new ViewModel class for the grid's rows and each column is a property of the ViewModel. So you can easily bind the data context of the row to that ViewModel and the columns to its properties.
I will post another answer since you edited masively the question.
If you use a datagrid in your view, the your view (XAML code) should look like this:
<Window x:Class="CareerTrackWpfClient.Views.User_Main_Window"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="User_Main_Window" Background="Black">
<Window>
<Grid HorizontalAlignment="Center" Visibility="Collapsed" Name="gridbooks">
<DataGrid Height="650" HorizontalAlignment="Stretch" Name="BooksGrid" RowHeight="90" ColumnWidth="200"
ColumnHeaderHeight="40" HeadersVisibility="Column" Background="Transparent" RowBackground="DarkGray"
AlternatingRowBackground="LightBlue" BorderBrush="Gray"
BorderThickness="2" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="Book #" Width="220" Binding="{Binding BookID}" >
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</DataGridTextColumn.ElementStyle>
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Book Title" Width="220" Binding="{Binding Title}" >
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</DataGridTextColumn.ElementStyle>
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
You should have a model like this
public class Book
{
public int BookID {get; set;}
public string Title {get;set;}
}
Now, either you do a ViewModel where you fill the list (like Zarzan said) - and this way you will respect the MVVM pattern. Or in the code beheind, in the constructor, bellow the InitializeComponent() method, you write something like this
List<Book> booksProvider=new List<Book>();
booksProvider.Add(new Book{BookID=1,"Book 1"}) ;
booksProvider.Add(new Book{BookID=2,"Book 2"}) ;
gridbooks.ItemsSource=booksProvider;
Is very nice using this UI component (DataGrid), from my point of view. It has the validations on place. Try to enter strings on the BookID field, and it will notify you. It is very flexible and you don't have to install anything.
Hope it helps as many developers as possible.

Categories