How can I reset the ThemeResource of DataTemplate? - c#

I have defined a DataTemplate that includes a textbox. In "glove mode" I need a large font/minHeight so the touch screen works nicely, but in "office mode" I want different set of values. I believe this should be possible, but can't figure it out.
How can I modify the theme in the code behind? Or if this is totally wrong, how should I do it?
Styles:
<Style x:Key="GloveTextBoxStyle" TargetType="TextBox">
<Setter Property="FontSize" Value="30"/>
<Setter Property="MinHeight" Value="60"/>
</Style>
<Style x:Key="OfficeTextBoxStyle" TargetType="TextBox">
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinHeight" Value="30"/>
</Style>
DataTemplate:
<DataTemplate x:Key="InspectionItemStringTemplate" x:DataType="data:InspectionItem"><TextBox Text="{x:Bind NewValue,Mode=TwoWay}"
x:Name="MyTextBox"
x:Phase="1" Style="{ThemeResource GloveTextBoxStyle}"/></DataTemplate>

What about IValueConverter?
You can create something like that:
public class TextBoxStyleConverter : IValueConverter
{
public Style GloveTextBoxStyle { get; set; }
public Style OfficeTextBoxStyle { get; set; }
public object Convert(object value, Type targetType, object parameter, string language)
{    
// analyze binded value and return needed style
return condition ? GloveTextBoxStyle : OfficeTextBoxStyle;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
and in your XAML code
<local:TextBoxStyleConverter x:Key="TextBoxStyleConverter">
<local:TextBoxStyleConverter.GloveTextBoxStyle>
<Style TargetType="TextBox">
<Setter Property="FontSize" Value="30"/>
<Setter Property="MinHeight" Value="60"/>
</Style>
</local:TextBoxStyleConverter.GloveTextBoxStyle>
<local:TextBoxStyleConverter.OfficeTextBoxStyle>
<Style TargetType="TextBox">
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinHeight" Value="30"/>
</Style>
</local:TextBoxStyleConverter.OfficeTextBoxStyle>
</local:TextBoxStyleConverter>
<DataTemplate x:Key="InspectionItemStringTemplate"
x:DataType="data:InspectionItem">
<TextBox Text="{x:Bind NewValue,Mode=TwoWay}"
x:Name="MyTextBox"
x:Phase="1"
Style="{Binding Converter={StaticResource TextBoxStyleConverter}}"/>
</DataTemplate>

Related

Conditional tooltip for DataGridTextColumn

I am currently facing a small issue with my DataGridTextColumn.
I want to display a tooltip at a DataGridTextColumn but only if the text is not empty.
How can I achieve this? The code that I am currently using:
<DataGridTextColumn IsReadOnly="True" Header="Person" Binding="{Binding SomeBinding, TargetNullValue='-'}" Width="Auto"
CellStyle="{StaticResource SomeStyle}"/>
With the style
<Style x:Key="SomeStyle"
TargetType="DataGridCell" BasedOn="{StaticResource InactiveStyle}">
<Style.Setters>
<Setter Property="ToolTip" Value="{Binding Path=SomeBinding}"/>
</Style.Setters>
</Style>
This code does provide me the tooltip, however, it is also showing the tooltip when there is no text. If there are any questions, please let me know and I can help you.
Try to add data triggers for string.Empty and null:
<Style x:Key="SomeStyle" TargetType="DataGridCell"
BasedOn="{StaticResource InactiveStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SomeBinding}" Value="">
<Setter Property="ToolTip" Value="{x:Null}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=SomeBinding}" Value="{x:Null}">
<Setter Property="ToolTip" Value="{x:Null}"/>
</DataTrigger>
</Style.Triggers>
<Style.Setters>
<Setter Property="ToolTip" Value="{Binding Path=SomeBinding}"/>
</Style.Setters>
</Style>
Here is an approach :
//Create a class which inherits from IValueConverter
public class CellToolTipConverter : IValueConverter
{
#region IValueConverter Membres
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string stringValue = (string)value;
if (!string.IsNullOrEmpty(stringValue))
return "Your tooltip";//As you are in a c# class, you have many possibilities.
else
return string.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
//In your xaml :
//Declare your namespace
xmlns:CustomClasses="clr-namespace:YourAssamblyName.YourNameSpaceOfConverterClass"
<UserControl.Resources>
<CustomClasses:CellToolTipConverter x:Key="CustomToolTipConverter"/>
</UserControl.Resources>
//In your grid view
<GridView.RowStyle>
<Style TargetType="{x:Type telerik:GridViewRow}">
<Setter Property="MyCustomToolTipProperty" Value="{Binding YourProperty, Converter=
{StaticResource CustomToolTipConverter}}"/>
</Style>
</GridView.RowStyle>

How to bind to another's object property with XAML Style Template?

Let's say I have the following class:
public class MyClass : System.Windows.FrameworkElement
{
public static readonly DependencyProperty HasFocusProperty = DependencyProperty.RegisterAttached("HasFocus", typeof(bool), typeof(MyClass), new PropertyMetadata(default(bool)));
public bool HasFocus
{
get => (bool)GetValue(HasFocusProperty);
set => SetValue(HasFocusProperty, value);
}
public System.Windows.Controls.TextBox TextBox { get; set; }
}
I want to change some UI properties of TextBox via XAML Template Trigger based on the property HasFocus, so I do the following:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:win="clr-namespace:System.Windows.Controls">
<Style TargetType="{x:Type win:TextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type win:TextBox}">
<ControlTemplate.Triggers>
<Trigger Property="MyClass.HasFocus" Value="True">
<Setter TargetName="Border" Property="BorderBrush" Value="Red" />
<Setter TargetName="Border" Property="BorderThickness" Value="2" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
However, the style is not applied when setting HasFocus = true.
Within the properties of TextBox, I can see that the trigger is registered. If I change <Trigger Property="MyClass.HasFocus" Value="True"> to <Trigger Property="MyClass.HasFocus" Value="False">, my style is applied initially. So I think my XAML definition is okay.
Any ideas how to solve this?
An element in a template that is applied to a TextBox cannot bind to a property of a MyClass, unless there is a MyClass element to bind to somewhere in the visual tree.
If you want to be able to set a custom HasFocus property of a TextBox, you should create an attached property:
public class FocusExtensions
{
public static readonly DependencyProperty SetHasFocusProperty = DependencyProperty.RegisterAttached(
"HasFocus",
typeof(bool),
typeof(FocusExtensions),
new FrameworkPropertyMetadata(false)
);
public static void SetHasFocus(TextBox element, bool value)
{
element.SetValue(SetHasFocusProperty, value);
}
public static bool GetHasFocus(TextBox element)
{
return (bool)element.GetValue(SetHasFocusProperty);
}
}
It can be set for any TextBox element:
<TextBox local:FocusExtensions.HasFocus="True">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="local:FocusExtensions.HasFocus" Value="True">
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="2" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>

WPF ListView Object DataTrigger

I'm trying to style a rectangle inside of a listview item, based on a data-field of the listview item object.
To return a boolean I'm converting the integer from daydata.workload to a boolean uasing a IValueConverter.
I'm getting no exception, the rectangle is just not affected by the DataTrigger. The other style rules are working fine.
<Window.Resources>
<cv:numConverter x:Key="capacityConverter" />
<Window.Resources>
-
<ListView Name="weekView" ItemsSource="{Binding dayList}" ItemTemplate="{StaticResource DefaultTemplate}" >
<ListView.Resources>
<Style TargetType="Rectangle" x:Key="capacityBG">
<Setter Property="Stroke" Value="#FFE2E2E2" />
<Setter Property="Width" Value="180" />
<Setter Property="Height" Value="10" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=dayList.workload, Converter={StaticResource capacityConverter}, ConverterParameter=12}">
<DataTrigger.Value>true</DataTrigger.Value>
<Setter Property="Fill" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Resources>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
-
<Rectangle Style="{DynamicResource capacityBG}" VerticalAlignment="Top" Grid.Row="0" />
-
public class numConverter : IValueConverter
{
object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((int)value) > val;
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public int val { get; set; }
}
-
public class dayData
{
public DateTime date { get; set; }
public int workload { get; set; }
public List<job> jobs { get; set; }
}
The problem here is that values entered for the converter parameter and the data trigger value are treated as string. You need to specify the type for each one of these values like shown below:
xmlns:sys="clr-namespace:System;assembly=mscorlib"
<Style.Triggers>
<DataTrigger>
<DataTrigger.Binding>
<Binding Path="WorkLoad" Converter="{StaticResource capacityConverter}">
<Binding.ConverterParameter>
<sys:Int32>12</sys:Int32>
</Binding.ConverterParameter>
</Binding>
</DataTrigger.Binding>
<DataTrigger.Value>
<sys:Boolean>true</sys:Boolean>
</DataTrigger.Value>
<Setter Property="Fill" Value="Red"/>
</DataTrigger>
</Style.Triggers>
Then you can cast converter parameter to an int to make the comparison.
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)value > (int)parameter;
}

Change windows style on click WPF

I want to change style on click, but simply cant find any way to do it.
Here is styles
<Style x:Key="CustomWindowStyle" TargetType="{x:Type Window}">
<Setter Property="WindowStyle" Value="None"/>
<Setter Property="AllowsTransparency" Value="True"/>
<Setter Property="MinWidth" Value="200"/>
<Setter Property="MinHeight" Value="46"/>
<!--CaptionHeight + ResizeBorderThickness * 2-->
<Setter Property="Background" Value="Yellow"/>
<Setter Property="BorderBrush" Value="Green"/>
<Setter Property="BorderThickness" Value="7"/>
<Setter Property="Foreground" Value="DarkRed"/>
<Setter Property="Template" Value="{StaticResource WindowTemplate}"/>
</Style>
<!--the red style window-->
<Style x:Key="RedWindowStyle" TargetType="{x:Type Window}">
<Setter Property="WindowStyle" Value="None"/>
<Setter Property="AllowsTransparency" Value="True"/>
<Setter Property="MinWidth" Value="100"/>
<Setter Property="MinHeight" Value="46"/>
<Setter Property="Background" Value="white"/>
<Setter Property="BorderBrush" Value="DarkRed"/>
<Setter Property="BorderThickness" Value="7"/>
<Setter Property="Foreground" Value="DarkGray"/>
<Setter Property="Template" Value="{StaticResource WindowTemplate}"/>
</Style>
I want to change it in MainWindow.xaml.cs
private void button_Click(object sender, RoutedEventArgs e)
{
this.Style ?? // dont know what to do
}
Please help
I would probably bind this in my style. For example:
MainWindow.xaml:
<Window x:Class="WpfApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="local:MainWindow">
<Setter Property="Background" Value="Red" />
<Style.Triggers>
<DataTrigger Binding="{Binding SomeProperty}" Value="True">
<Setter Property="Background" Value="Blue" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Button HorizontalAlignment="Center" VerticalAlignment="Center" Click="Button_Click">Click Me, Yo!</Button>
</Grid>
</Window>
MainWindow.xaml.cs:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace WpfApp2
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _someProperty;
public bool SomeProperty
{
get { return _someProperty; }
set
{
_someProperty = value;
OnPropertyChanged();
}
}
public MainWindow()
{
DataContext = this;
InitializeComponent();
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void Button_Click(object sender, RoutedEventArgs e)
{
SomeProperty = !SomeProperty;
}
}
}
Essentially, you derive the state of your window from some property in your ViewModel, using a converter if needed.
Of course, if you want a direct solution, you could just use FindResource:
this.Style = this.FindResource("MyLocalStyleResourceName") as Style;
This is described here.

How to set color for first row in Datagrid

I have a datagrid on a wpf (mvvm) project.
the datagrid sort is showing the last item added to the collection as the first row.
i want to color the first row (in order to highlight new item added to the collection)
I've seen some similar questions about this manner but none of them are really related to what i am looking for.
i have tried to use a IValueConverter but it doesnt seems to be the right path for me as i need to get a unique identifier for the first row and change all the rest of the rows in order to classified it as a "First Row".
my object model for the items in the collection looks like this:
public class Messages
{
public string Date {get; set;}
public string Sender{get; set;}
public string Content{get; set;}
}
*EDIT
Forgot to add the converter code...
of course this will color all rows to red, as i dont know how to affect the other rows when the collection changes.
class DateToColorConverter : IValueConverter
{
object IValueConverter.Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (Convert.ToDateTime(value) >= DateTime.Now.AddMinutes(-1))
{
return "Red";
}
else
return "Yellow";
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
You can use RelativeSource with Mode set to PreviousData to identify whether dataGrid row is first one or not. For first row PreviousData will return null.
Apply DataTrigger on DataGridRow in ItemContainerStyle:
<DataGrid>
<DataGrid.ItemContainerStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="LightBlue"/>
<Style.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource Mode=PreviousData}}"
Value="{x:Null}">
<Setter Property="Background" Value="Green"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.ItemContainerStyle>
</DataGrid>
You could do a simple solution if your data classes use some sort of an Id field. Let's say that newly added objects have an Id of -1, or 00000000-0000-0000-0000-000000000000 (to be updated/set upon a successful save). In this case, you could use a simple DataTrigger to change the Background of these items:
<DataTemplate DataType="{x:Type YourPrefix:Message}">
<Border>
<!--Define your Message UI here-->
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="Background" Value="{StaticResource YourNormalBrush}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Id}" Value="-1">
<Setter Property="Background" Value="{StaticResource YourHighlightBrush}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
</DataTemplate>
Add a bool to your Model that will indicate the last element added :
public class Messages
{
public string Date {get; set;}
public string Sender{get; set;}
public string Content{get; set;}
public string IsNew {get; set;}
}
and set the style of the DataGridRow based on that Property :
<Window.Resources>
<Style x:Key="DataGridRowStyle" TargetType="DataGridRow">
<Setter Property="Background" Value="Blue"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsNew}" Value="True">
<Setter Property="Background" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
In code above the blue will be the default style and the Red is for the new Rows
<DataGrid ItemsSource="{Binding DataGridItems}" RowStyle="{StaticResource DataGridRowStyle}" AutoGenerateColumns="True">
</DataGrid>

Categories