IDateErrorInfo Multi Validation.ErrorTemplate - c#

I have implemented IDataErrorInfo to my custom Controls' ViewModels. Everything works fine (border is being drawn red, a tooltip with the error is being shawn), but i was wondering if there is a way to have two different Validation.ErrorTemplates for errors and warnings.
My Custom Control Style (with the Validation.ErrorTemplate)
<Style TargetType="{x:Type controls:CustomTextBoxNumeric}">
<Setter Property="TextAlignment" Value="Right"/>
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
My Custom Control ViewModel (INotifyPropertyChanged is implemented at the base ViewModel)
public class CustomTextBoxNumericViewModel : BaseComponentViewModel, IDataErrorInfo
{
private decimal? decimalValue;
private bool hasErrors;
private bool hasWarnings;
public CustomTextBoxNumericViewModel()
{
}
[DataMember(EmitDefaultValue = false)]
public decimal? DecimalValue
{
get { return this.decimalValue; }
set { this.decimalValue = value; this.Changed("DecimalValue"); this.Changed("HasErrors"); }
}
[DataMember(EmitDefaultValue = false)]
public bool HasErrors
{
get { return this.hasErrors; }
set { this.hasErrors = value; this.Changed("HasErrors"); this.Changed("DecimalValue"); }
}
[DataMember(EmitDefaultValue = false)]
public bool HasWarnings
{
get { return this.hasWarnings; }
set { this.hasWarnings = value; this.Changed("HasWarnings"); this.Changed("DecimalValue"); }
}
#region IDataErrorInfo Implementation
public string Error
{
get
{
throw new NotImplementedException();
}
}
public string this[string propertyName]
{
get
{
if (propertyName == "DecimalValue")
{
if (HasErrors)
{
return this.ErrorsField;
}
if (HasWarnings)
{
return this.WarningsField;
}
if (DecimalValue < 0)
{
return "Must be greater than 0";
}
}
return string.Empty;
}
}
#endregion
}

I managed to solve my issue using ControlTemplate resources.
My style changed to:
<Style TargetType="{x:Type controls:CustomTextBoxNumeric}">
<Setter Property="TextAlignment" Value="Right"/>
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
</Trigger>
<DataTrigger Binding="{Binding Path=ViewModel.HasWarnings, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Validation.ErrorTemplate" Value="{DynamicResource EntypoWarningTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=ViewModel.HasErrors, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Validation.ErrorTemplate" Value="{DynamicResource EntypoErrorTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
The ControlTemplates:
<ControlTemplate x:Key="MyErrorTemplate" TargetType="{x:Type Control}">
<DockPanel LastChildFill="True">
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
<ControlTemplate x:Key="MyWarningTemplate" TargetType="{x:Type Control}">
<DockPanel LastChildFill="True">
<Border BorderBrush="Orange" BorderThickness="1">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>

Related

XAML: Show button on ComboBox item highlight

I have a ComboBox with an ItemTemplateSelector, using 2 different DataTemplates, one for when its drop down is visible and another when it is not. For the drop down template, each ComboBox item is represented by a TextBlock and a Button that should only be visible whenever that item is focused/highlighted/mouse over. This is what I've tried:
<ComboBox x:Name="Windows" ItemsSource="{Binding Windows}" SelectedItem="{Binding Window}" Focusable="False" MaxDropDownHeight="238">
<ComboBox.ItemTemplateSelector>
<s:ComboBoxItemTemplateSelector>
<s:ComboBoxItemTemplateSelector.SelectedTemplate>
<DataTemplate>
<TextBlock Text="{Binding TitleShort}" ToolTip="{Binding Title}" />
</DataTemplate>
</s:ComboBoxItemTemplateSelector.SelectedTemplate>
<s:ComboBoxItemTemplateSelector.DropDownTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="{Binding TitleShort}" />
<Button Content="X">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsFocused, ElementName=Windows}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
</DataTemplate>
</s:ComboBoxItemTemplateSelector.DropDownTemplate>
</s:ComboBoxItemTemplateSelector>
</ComboBox.ItemTemplateSelector>
<ComboBox.ItemContainerStyle>
<Style BasedOn="{StaticResource MaterialDesignComboBoxItemStyle}" TargetType="ComboBoxItem">
<Setter Property="ToolTip">
<Setter.Value>
<TextBlock Text="{Binding Title}" />
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
public class ComboBoxItemTemplateSelector : DataTemplateSelector
{
public DataTemplate SelectedTemplate { get; set; }
public DataTemplate DropDownTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
ComboBoxItem comboBoxItem = GetVisualParent<ComboBoxItem>(container);
if (comboBoxItem == null)
{
return SelectedTemplate;
}
return DropDownTemplate;
}
private static T GetVisualParent<T>(object childObject) where T : Visual
{
DependencyObject child = childObject as DependencyObject;
while ((child != null) && !(child is T))
{
child = VisualTreeHelper.GetParent(child);
}
return child as T;
}
}
ComboBox generates ComboBoxItem as a container for every item in its itemssource. You can bind to its properties with RelativeSource binding.
This should get you the expected behavior:
<Button Content="X">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource AncestorType=ComboBoxItem}}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>

Passing specific parameters borderbrush to custom template

WPF, I have custom template and I want to pass borderbrush to this templete from xaml window I Created DependencyProperty
public class PassParamBrush
{
public static DependencyProperty BorderBrushProperty = DependencyProperty.RegisterAttached
(
"BorderBrush",
typeof(SolidColorBrush),
typeof(PassParamBrush),
new PropertyMetadata(null)
);
public static SolidColorBrush GetBorderBrush(DependencyObject target)
{
return (SolidColorBrush)target.GetValue(BorderBrushProperty);
}
public static void SetBorderBrush(DependencyObject target, ImageSource value)
{
target.SetValue(BorderBrushProperty, value);
}
}
And in UI I call it in this way
xmlns:localOne="clr-namespace:Turk_Common_WPF_Style.Style.WindowsStyle"
<Button localOne:PassParamBrush.BorderBrush="#aadbff" Width="30" Height="30" Style="{DynamicResource btnLogout}" />
And here the template
<!--Buton With Image background-->
<Style x:Key="btnLogout" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border CornerRadius="150" BorderBrush="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(local:PassParamToTemplter.PassParamBrush.BorderBrush)}" BorderThickness="5">
<!--<Image Source="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(local:PassParamToTemplter.ImageSource)}"
Margin="2" Stretch="Fill" RenderOptions.BitmapScalingMode="HighQuality"/>-->
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="0" Color="Gray" Opacity="1" BlurRadius="25" />
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="Button.IsPressed" Value="True">
<Setter Property="Margin" Value="7,7,7,0" />
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="0" Color="#E8AA6E" Opacity="1" BlurRadius="25" />
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
but still not working when i run the code and show me this error
**+ InnerException {"No imaging component suitable to complete this operation was found."} System.Exception {System.NotSupportedException}
**
I found the error
public class PassParamBrush{
public static DependencyProperty BorderBrushProperty = DependencyProperty.RegisterAttached
(
"BorderBrush",
typeof(SolidColorBrush),
typeof(PassParamBrush),
new PropertyMetadata(null)
);
public static SolidColorBrush GetBorderBrush(DependencyObject target)
{
return (SolidColorBrush)target.GetValue(BorderBrushProperty);
}
public static void SetBorderBrush(DependencyObject target, ImageSource value)
{
target.SetValue(BorderBrushProperty, value);
}
}
the error her i should change the format in SetBorderBrush from ImageSource To SolidColorBrush

Binding BorderBackground in ListboxItem

I tried different ways to solve the Problem. First I bound in my ViewModel the MainColor as String,Color,Brush and tried to bind it to the Border -> don't worked for me. Second was an Converter, but I couldn't bind correct the string to activate the converter. The only thing that worked with the Bind the MainColor as string to a Label style, that I have also in the same Resource. So now the change in the Label works, but not in the Listbox.
My View:
<Page.Resources>
<ResourceDictionary Source="/Resources/ResourcePageCuttingData.xaml"></ResourceDictionary>
</Page.Resources>
<ListBox ItemsSource="{Binding Material}" SelectedItem="{Binding SelectedMat, Mode=TwoWay}" Style="{StaticResource styleMat}" Grid.Column="5" Grid.Row="1" Margin="20,0"></ListBox>
<Label Style="{StaticResource Label_Search}" Content="{DynamicResource so_lbl_cm}" ></Label>
My Listbox in ResourceDictonary:
<Style x:Key="styleMat" TargetType="{x:Type ListBox}">
<Setter Property="OverridesDefaultStyle" Value="True"></Setter>
<Setter Property="ItemContainerStyle" Value="{DynamicResource ResourceKey=styleLocMschItem}"></Setter>
<Setter Property="ItemTemplate" Value="{StaticResource ResourceKey=ResultMatDataTemplate}"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<Border BorderBrush="#5A5A5A" BorderThickness="1" CornerRadius="4">
<ScrollViewer Margin="1">
<ItemsPresenter Margin="1"></ItemsPresenter>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate x:Key="ResultMatDataTemplate">
<TextBlock Text="{Binding}"
FontSize="20"
></TextBlock>
</DataTemplate>
<Style x:Key="styleLocMachItem" TargetType="{x:Type ListBoxItem}">
<Setter Property="OverridesDefaultStyle" Value="True"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Grid x:Name="grid">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Border x:Name="hover"
Background="YellowGreen"
Visibility="Collapsed">
</Border>
<Border x:Name="orginal"
Background="{Binding MainColor}">
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="highlight"
Property="Visibility"
Value="Visible">
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The MainColor is not working in Border x:Name="original"
My Label:
<Style x:Key="Label_Search" TargetType="{x:Type Control}">
<Setter Property="Background" Value="{Binding MainColor}" />
</Style>
Here the MainColor works fine
My ViewModel:
public CuttingSpeed_ViewModel()
{
switch (value)
{
case "Fast":
MainColor = "Pink";
break;
case "Slow":
MainColor = "Yellow";
break;
default:
MainColor ="Red";
break;
}}
private string _MainColor;
public string MainColor
{
get { return _MainColor; }
set { _MainColor = value; OnPropertyChanged("MainColor"); }
}
public IEnumerable<string> Material
{
get { return _Material; }
set { _Material = value; OnPropertyChanged("Material"); }
}
private string _SelectedMat;
public string SelectedMat
{
get { return _SelectedMat; }
set
{..}}
I think there should be something in xaml that don't allow me to get the value MainColor.
The MainColor property should return a Brush:
public CuttingSpeed_ViewModel()
{
switch (value)
{
case "Fast":
MainColor = Brushes.Pink;
break;
case "Slow":
MainColor = Brushes.Yellow;
break;
default:
MainColor = Brushes.Red;
break;
}
}
private Brush _MainColor;
public Brush MainColor
{
get { return _MainColor; }
set { _MainColor = value; OnPropertyChanged("MainColor"); }
}
You should also bind to the DataContext of the parent ListBox:
<Border x:Name="orginal" Background="{Binding DataContext.MainColor,
RelativeSource={RelativeSource AncestorType=ListBox}}">

Dynamic DataTemplate in ControlTemplate via Trigger

I have a listbox "listBox_Results" and several ItemTemplates(one of them ItemTemplateStyle1), in my ItemContainerStyle I'm setting Template property for item. So I want change my ItemTemplate in trigger "IsSelected".
(in common sense: I want my listboxitem change size and content display on selection, by dynamicly setting diffrent ItemTemplate)
Do you have any solutions?
Best regards
upd:If you think this question is unclear or not useful, most apreciate if tell you me why, before you minus
Code:
<ListBox Name="listBox_Results"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
BorderThickness="0"
Margin="2"
Grid.Row="0"
ItemTemplate="{StaticResource ItemTemplateStyle1}"
ItemsSource="{Binding}" >
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Padding" Value="2,2,2,2"/>
<Setter Property="Margin" Value="2"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="Bd" Margin="1" SnapsToDevicePixels="true" CornerRadius="3" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" >
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Background" TargetName="Bd">
<Setter.Value>
#E1E1E1
</Setter.Value>
</Setter>
...
First, take out your inline styling and create a ResourceDictionary to keep things together. This will also help with the template switch I am suggesting.
In the Resource Dictionary, you will define the two templates that you want (the selected and unselected list item templates), the style of the list item and the list box itself. I am abbreviating the code, just to show how I would put the items together.
In the ResourceDictionary
<ControlTemplate x:Key="unselectedTemplate" TargetType="{x:Type ListBoxItem}">
<Grid>
<ContentPresenter />
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="selectedTemplate" TargetType="{x:Type ListBoxItem}">
<Grid>
<ContentPresenter Margin="3"/>
</Grid>
</ControlTemplate>
<Style x:Key="listboxItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Template" Value="{StaticResource unselectedTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Template" Value="{StaticResource selectedTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="listBoxStyle" TargetType="{x:Type ListBox}">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="ItemContainerStyle" Value="{StaticResource listboxItemStyle}"/>
</Style>
Then, when you are creating your list box on the page... just reference the list box style key.
<ListBox Name="listbox_Results" Style="{StaticResource listBoxStyle}" ItemsSource="{Binding}"/>
Make sure the ControlTemplates are defined before the styles, I found when I don't I run into errors. Also, this keeps your layout page cleaner, and the styles are easier to reuse if you need to use them again.
I uploaded a very basic example here.
You have to use an Data template selector which will select a particular data template according to the conditions in your selector.
You have to write the data templates in xaml with separate names and select them from the DataTemplateSelector class file.
For that you need to inherit from the base class DataTemplateSelector
I will share some sample code with you. Please check this and you will get an idea of how to use an item template selector.
XAML :
<Window x:Class="WpfApplication5.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication5"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <DataTemplate x:Key="NormalUserDataTemplate">
            <StackPanel>
                <TextBlock Text="{Binding Name}" />
            </StackPanel>
        </DataTemplate>
        <DataTemplate x:Key="PremiumUserDataTemplate">
            <StackPanel Background="LightBlue">
                <TextBlock Text="{Binding Name}" />
            </StackPanel>
        </DataTemplate>
        <local:PremiumUserDataTemplateSelector x:Key="myPremiumUserDataTemplateSelector" />
    </Window.Resources>
    <Grid>
        <ListView x:Name="myListView" ItemTemplateSelector="{StaticResource myPremiumUserDataTemplateSelector}">
        </ListView>
    </Grid>
</Window>
Code behind :
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<User> users = new List<User>();
for (int i = 0; i < 10; ++i)
{
var user = new User { ID = i, Name = "Name " + i.ToString(), Age = 20 + i };
if (i == 2 || i == 4)
{
user.IsPremiumUser = true;
}
users.Add(user);
}
myListView.ItemsSource = users;
}
}
public class PremiumUserDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
FrameworkElement elemnt = container as FrameworkElement;
User user = item as User;
if(user.IsPremiumUser)
{
return elemnt.FindResource("PremiumUserDataTemplate") as DataTemplate;
}
else
{
return elemnt.FindResource("NormalUserDataTemplate") as DataTemplate;
}
}
}
public class User
{
public int ID { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public bool IsPremiumUser { get; set; }
}

How i can bind StaticResource key in View model

I have different ControlTemplates for one Canvas:
<Application.Resources>
<ControlTemplate x:Key="Control1" />
<ControlTemplate x:Key="Control2" />
</Application.Resources>
I want to change one of them by my viewmodel property like this:
private string _template = "Control1";
public string Template
{
get
{
return _template;
}
set
{
if (!string.IsNullOrEmpty(value))
{
_template = value;
OnPropertyChanged("Template");
}
}
}
And finally use it in my view:
<UserControl Template="{StaticResource {Binding Template}}" />
But it doesn't work, how i can fix it?
Thanks
You can try using DataTriggers
<UserControl>
<UserControl.Style>
<Style TargetType="{x:Type UserControl}">
<Setter Property="ControlTemplate" value="{StaticResource Control1}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Template}" Value ="Control1">
<Setter Property="ControlTemplate" value="{StaticResource Control1}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Template}" Value ="Control2">
<Setter Property="ControlTemplate" value="{StaticResource Control2}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Style>
</UserControl>

Categories

Resources