I have an Expander that contains 3 checkbox.
<Expander Header="Bind Here" Grid.Row="3" Grid.ColumnSpan="2">
<StackPanel >
<CheckBox>Test 1</CheckBox>
<CheckBox>Test 2</CheckBox>
<CheckBox>Test 3</CheckBox>
</StackPanel>
</Expander>
I want to append the text of a checked checkbox to the Header of the expander.
Is it possible using binding?
Thanks for the answers but here's what I did:
<Expander Header="{Binding SelectedTitle}" Grid.Row="3" Grid.ColumnSpan="2">
<StackPanel >
<CheckBox IsChecked="{Binding Path=SelectedItem.isTest1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">Test 1</CheckBox>
<CheckBox IsChecked="{Binding Path=SelectedItem.isTest2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">Test 2</CheckBox>
<CheckBox IsChecked="{Binding Path=SelectedItem.isTest3, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">Test 3</CheckBox>
</StackPanel>
public String SelectedTitle
{
get
{
StringBuilder builder = new StringBuilder();
if (SelectedItem.isTest1)
builder.Append("Browse subfolders, ");
if (SelectedItem.isTest2)
builder.Append("Enter filename at the panel, ");
if (SelectedItem.isTest3)
builder.Append("Always show scan settings, ");
if (builder.Length == 0)
builder.Append("None");
return builder.ToString().Trim().TrimEnd(',');
}
}
private void SelectedItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
UpdateProperty("SelectedTitle");
}
When your checkbox is checked you need to track if it has been checked or not, you can do this either by binding or event handling or triggers.
Following is a quick example
Using Binding (XAML Only)
if you are keen to have it as binding you can try having triggers and setters on control template for expander like this (I leave it to user to edit and produce a minimal xaml the following piece works but may not be minimal) --
<Window.Resources>
<ControlTemplate x:Key="ControlTemplate" TargetType="Expander">
<Expander x:Name="MyExpander">
<StackPanel Orientation="Vertical">
<CheckBox x:Name="Box1">Test 1</CheckBox>
<CheckBox x:Name="Box2">Test 2</CheckBox>
<CheckBox x:Name="Box3"> Test 3</CheckBox>
</StackPanel>
</Expander>
<ControlTemplate.Triggers>
<Trigger SourceName="Box1" Property="CheckBox.IsChecked" Value="True">
<Setter Property="Header"
TargetName="MyExpander"
Value="{Binding Content, ElementName=Box1 }" />
</Trigger>
<Trigger SourceName="Box2"
Property="CheckBox.IsChecked"
Value="True">
<Setter Property="Header" TargetName="MyExpander"
Value="{Binding Content, ElementName=Box2 }" />
</Trigger>
<Trigger SourceName="Box3"
Property="CheckBox.IsChecked"
Value="True">
<Setter Property="Header"
TargetName="MyExpander"
Value="{Binding Content, ElementName=Box3 }" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Window.Resources>
<Expander Template="{StaticResource ControlTemplate}"/>
or if you don't mind code behind
Code Behind Way
private void ToggleButton_OnChecked(object sender, RoutedEventArgs e)
{
var checkBox = ((CheckBox) sender);
if (checkBox.IsChecked != null && checkBox.IsChecked.Value)
{
MyExpander.Header = checkBox.Content.ToString();
}
}
Xaml --
<Expander x:Name="MyExpander">
<StackPanel>
<CheckBox Checked="ToggleButton_OnChecked">Test 1</CheckBox>
<CheckBox Checked="ToggleButton_OnChecked">Test 2</CheckBox>
<CheckBox Checked="ToggleButton_OnChecked">Test 3</CheckBox>
</StackPanel>
</Expander>
Here is a cool way to do so:(Binding + Converter)
XAML:
<Expander Grid.Row="3" Grid.ColumnSpan="2" Name="expander" Width="500">
<Expander.Resources>
<local:ExpanderHeaderConverter x:Key="ExpanderHeaderConverter" />
</Expander.Resources>
<Expander.Header>
<TextBlock >
<TextBlock.Text>
<MultiBinding Converter="{StaticResource ExpanderHeaderConverter}">
<Binding ElementName="c1" Mode="OneWay"/>
<Binding ElementName="c1" Mode="OneWay" Path="IsChecked"/>
<Binding ElementName="c2" Mode="OneWay"/>
<Binding ElementName="c2" Mode="OneWay" Path="IsChecked"/>
<Binding ElementName="c3" Mode="OneWay"/>
<Binding ElementName="c3" Mode="OneWay" Path="IsChecked"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Expander.Header>
<StackPanel>
<CheckBox Name="c1" >Test 1</CheckBox>
<CheckBox Name="c2">Test 2</CheckBox>
<CheckBox Name="c3">Test 3</CheckBox>
</StackPanel>
</Expander>
Converter:
public class ExpanderHeaderConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string output = string.Empty;
for (int i = 0; i < values.Length; i+=2)
{
if ((values[i + 1] as bool?) == true)
output += (values[i] as CheckBox).Content.ToString()+" ";
}
return output;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Related
I'm making an application to generate recipes for a specified period (for example a week). As part of this application I have to make a list of ingredients. These ingredients can be specified by the user so I made a UserControl for adding an ingredient. This UserControl contains 6 TextBox-es and a Button. What I'm trying to do is when the user click on the button, the ingredient specified by the user (and by the TextBoxes) will be added to the list of ingredients.
I'm using MVVM architecture and a MultiValueConverter to bind the TextBoxes to the Button. I noticed that the Convert method was called only at the instantiation, but not when I click the Button.
How can I solve this problem still using MVVM?
Thanks for your advice and time!
The corresponding code snippets:
AddIngredientView.xaml:
<UserControl.Resources>
<ResourceDictionary>
<converters:IngredientConverter x:Key="IngredientConverter"/>
</ResourceDictionary>
</UserControl.Resources>
<StackPanel>
<TextBlock Text="Étel hozzáadása" Foreground="White" FontSize="28" FontWeight="Bold" HorizontalAlignment="Center" Margin="0,0,0,20"/>
<TextBox Name="nameTextBox" ToolTip="Név" Width="250" Height="40" VerticalContentAlignment="Center" HorizontalAlignment="Center" Margin="5" Style="{StaticResource ModernTextbox}"/>
<TextBox Name="weightTextBox" ToolTip="Tömeg (g)" Width="250" Height="40" VerticalContentAlignment="Center" HorizontalAlignment="Center" Margin="5" Style="{StaticResource ModernTextbox}"/>
<TextBox Name="energyTextBox" ToolTip="Energia (kcal)" Width="250" Height="40" VerticalContentAlignment="Center" HorizontalAlignment="Center" Margin="5" Style="{StaticResource ModernTextbox}"/>
<TextBox Name="carbohydrateTextBox" ToolTip="Szénhidrát (g)" Width="250" Height="40" VerticalContentAlignment="Center" HorizontalAlignment="Center" Margin="5" Style="{StaticResource ModernTextbox}"/>
<TextBox Name="proteinTextBox" ToolTip="Fehérje (g)" Width="250" Height="40" VerticalContentAlignment="Center" HorizontalAlignment="Center" Margin="5" Style="{StaticResource ModernTextbox}"/>
<TextBox Name="fatTextBox" ToolTip="Zsír (g)" Width="250" Height="40" VerticalContentAlignment="Center" HorizontalAlignment="Center" Margin="5" Style="{StaticResource ModernTextbox}"/>
<Button Content="További értékek" Foreground="#353340" FontSize="14" Margin="0,10,60,0">
<Button.Style>
<Style TargetType="Button">
<Setter Property="TextElement.FontFamily" Value="Fonts/#Poppins"/>
<Setter Property="Background" Value="DarkGray"/>
<Setter Property="Cursor" Value="Hand"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#353340"/>
<Setter Property="Foreground" Value="DarkGray"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Width="130" Height="25" CornerRadius="12" Background="{TemplateBinding Background}">
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
<Button Content="Hozzáadás" Foreground="#353340" FontSize="14" Margin="500,50,0,0" Command="{Binding AddIngredientCommand}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="TextElement.FontFamily" Value="Fonts/#Poppins"/>
<Setter Property="Background" Value="DarkGray"/>
<Setter Property="Cursor" Value="Hand"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#353340"/>
<Setter Property="Foreground" Value="DarkGray"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Width="100" Height="25" CornerRadius="12" Background="{TemplateBinding Background}">
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
</ControlTemplate>
</Button.Template>
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource IngredientConverter}" UpdateSourceTrigger="PropertyChanged">
<Binding Path="Text" ElementName="nameTextBox"/>
<Binding Path="Text" ElementName="weightTextBox"/>
<Binding Path="Text" ElementName="energyTextBox"/>
<Binding Path="Text" ElementName="carbohydrateTextBox"/>
<Binding Path="Text" ElementName="proteinTextBox"/>
<Binding Path="Text" ElementName="fatTextBox"/>
</MultiBinding>
</Button.CommandParameter>
</Button>
</StackPanel>
IngredientConverter.cs:
public class IngredientConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return String.Concat(values[0], ",", values[1], ",", values[2], ",", values[3], ",", values[4], ",", values[5]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return (value as string).Split(',');
}
}
AddIngredientViewModel.cs:
public class AddIngredientViewModel : ViewModelBase
{
#region Fields
private YazioRecipesAddOnModel _model;
#endregion
#region Properties
public DelegateCommand AddIngredientCommand { get; private set; }
#endregion
#region Constructor
public AddIngredientViewModel(YazioRecipesAddOnModel model)
{
_model = model;
AddIngredientCommand = new DelegateCommand(param => AddIngredient(param.ToString()));
}
#endregion
#region Public methods
public void AddIngredient(String ingredientString)
{
if (ingredientString == null)
return;
_model.AddIngredient(ingredientString);
}
#endregion
}
You should bind each TextBox to a different source property of the view model e.g.:
<TextBox Name="weightTextBox" Text="{Binding Weight}" />
You could then simply access the values directly in your AddIngredient method:
string ingredientString = string.Concat(this.Weight, "," ...);
_model.AddIngredient(ingredientString);
One of the key aspects of MVVM is to implement the application logic in the view models. Don't use converters for this.
I am trying to add a new column in a calendar control to add the week number. I have found a example that works perfect, the problem is very simply and it uses code behind, and I am wanted to use MVVM and a converter in another class, no inside the code behind.
Then example that I have found is this:
<Window x:Class="CalendarioNumeroSemana.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:CalendarioNumeroSemana"
xmlns:app="clr-namespace:CalendarioNumeroSemana"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Calendar Grid.IsSharedSizeScope="True" HorizontalAlignment="Left" Margin="209,116,0,0" VerticalAlignment="Top">
<Calendar.CalendarDayButtonStyle>
<Style TargetType="{x:Type CalendarDayButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=(Grid.Column),RelativeSource={RelativeSource Mode=Self}}"
Value="0">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid HorizontalAlignment="Right">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="XX"/>
<ColumnDefinition SharedSizeGroup="YY"/>
</Grid.ColumnDefinitions>
<Border BorderThickness="0,0,0,0" BorderBrush="Black" Margin="-15,0,0,0">
<TextBlock Margin="0,0,2,0" FontWeight="Bold">
<TextBlock.Text>
<Binding Path="DataContext">
<Binding.Converter>
<app:WeekNumberConverter />
</Binding.Converter>
<Binding.RelativeSource>
<RelativeSource Mode="FindAncestor"
AncestorType="{x:Type CalendarDayButton}"/>
</Binding.RelativeSource>
</Binding>
</TextBlock.Text>
</TextBlock>
</Border>
<TextBlock Text="{Binding }" Grid.Column="1" HorizontalAlignment="Center"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Calendar.CalendarDayButtonStyle>
</Calendar>
</Grid>
</Window>
namespace CalendarioNumeroSemana
{
/// <summary>
/// Lógica de interacción para MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class WeekNumberConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is DateTime)
{
DateTime dt = (DateTime)value;
return getNumeroSemenaIso8601(dt);
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
private int getNumeroSemenaIso8601(DateTime paramFecha)
{
System.Globalization.Calendar miCalendario = CultureInfo.InvariantCulture.Calendar;
DateTime miDtFechaBase = paramFecha;
DayOfWeek miDiaSemana = miCalendario.GetDayOfWeek(paramFecha);
if (miDiaSemana == DayOfWeek.Monday) { miDtFechaBase = miDtFechaBase.AddDays(3); }
if (miDiaSemana == DayOfWeek.Tuesday) { miDtFechaBase = miDtFechaBase.AddDays(2); }
if (miDiaSemana == DayOfWeek.Wednesday) { miDtFechaBase = miDtFechaBase.AddDays(1); }
if (miDiaSemana == DayOfWeek.Friday) { miDtFechaBase = miDtFechaBase.AddDays(-1); }
if (miDiaSemana == DayOfWeek.Saturday) { miDtFechaBase = miDtFechaBase.AddDays(-2); }
if (miDiaSemana == DayOfWeek.Sunday) { miDtFechaBase = miDtFechaBase.AddDays(-3); }
return miCalendario.GetWeekOfYear(miDtFechaBase, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
}
}
}
But I would like to use this in my application, that it uses MVVM and I have the converter to get the week number in another class.
My axml is this:
<Calendar Grid.IsSharedSizeScope="True" HorizontalAlignment="Left" Margin="5,5,5,5" Padding="0,0,0,0" VerticalAlignment="Top">
<Calendar.CalendarDayButtonStyle>
<Style TargetType="{x:Type CalendarDayButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=(Grid.Column),RelativeSource={RelativeSource Mode=Self}}"
Value="0">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid HorizontalAlignment="Right">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="XX"/>
<ColumnDefinition SharedSizeGroup="YY"/>
</Grid.ColumnDefinitions>
<Border BorderThickness="0,0,0,0" BorderBrush="Black" Margin="-15,0,0,0">
<TextBlock Margin="0,0,2,0" FontWeight="Bold">
<TextBlock.Text>
<Binding Path="DataContext">
<Binding.Converter>
<app:WeekNumberConverter />
</Binding.Converter>
<Binding.RelativeSource>
<RelativeSource Mode="FindAncestor"
AncestorType="{x:Type CalendarDayButton}"/>
</Binding.RelativeSource>
</Binding>
</TextBlock.Text>
</TextBlock>
</Border>
<TextBlock Text="{Binding }" Grid.Column="1" HorizontalAlignment="Center"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Calendar.CalendarDayButtonStyle>
</Calendar>
My main problem is that I don't know how to change the code of binding.text, and later, in TextBlock Text, it uses Binding, but I don't know what binding is this.
I have tried something like that:
<TextBlock.Text>
<Binding Converter="{StaticResource NumeroSemanaValueConverter}"/>
</TextBlock.Text>
But in the converter I don't get a date, I g et the day of the first button. I need the date.
So how could I adapt the original code to my case?
Thanks.
This has nothing to do with MVVM. It's pure control logic. The template of the control uses a converter to get the week number. A converter is just a class and a template is just a template.
So the example you have found can certainly be used in a MVVM application. It's just a built-in control with a custom template. No view model or logic that should belong to a view model is involved as far as I can see.
I would to like to enable a Button for a specific row of a DataGrid and disable it for the other rows in code behind. The thing is the IsEnabled property impact all the buttons in all the rows which is not what I want. It cannot have two different values (visible for some rows and Collapsed for others in the same time)
Can you help me please ?
here is my xaml :
<DataGrid ItemsSource="{Binding TaskUsers}" Name="DgUser" AutoGenerateColumns="False" IsReadOnly="True" SelectionUnit="FullRow" SelectionMode="Single" CanUserAddRows="False" CanUserSortColumns="False" SelectedValuePath="TaskUserId" >
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<Button BorderThickness="0" ToolTip="Subscribe" Click="AddTaskUser_OnClick">
<Image Source="/Sinergi;component/Images/add-icon16.png"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button BorderThickness="0" ToolTip="Delete a User" Click="DeleteTaskUser_OnClick" Name="DeleteUserButton" Loaded="DeleteUserButton_OnLoaded">
<Image Source="/Sinergi;component/Images/Trash-can-icon16.png" />
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="UserName" Binding="{Binding User.Name}" />
</DataGrid.Columns>
here is my code behind code
private void DeleteUserButton_OnLoaded(object sender, RoutedEventArgs e)
{
var delete = sender as Button;
if (delete == null) return;
foreach (var item in DgUser.Items)
{
var o = (TaskUser) item;
if (o.UserId == Identity.This.OnBehalfOfUser.UserId) continue;
delete.IsEnabled = false;
delete.Visibility = Visibility.Collapsed;
}
}
You should bind the IsEnabled property of the Button to a property of your TaskUser object and get rid of the DeleteUserButton_OnLoaded event handler:
<DataTemplate>
<Button BorderThickness="0" ToolTip="Delete a User" Click="DeleteTaskUser_OnClick" Name="DeleteUserButton">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Style.Triggers>
<DataTrigger Binding="{Binding UserId}" Value="{x:Static local:Identity.This.OnBehalfOfUser.UserId}">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<Button.Content>
<Image Source="/Sinergi;component/Images/Trash-can-icon16.png" />
</Button.Content>
</Button>
</DataTemplate>
You need to set the Value of the DataTrigger to whatever Identity.This.OnBehalfOfUser.UserId is, or use a value converter:
<DataTemplate>
<DataTemplate.Resources>
<local:Converter x:Key="converter" />
</DataTemplate.Resources>
<Button BorderThickness="0" ToolTip="Delete a User" Click="DeleteTaskUser_OnClick" Name="DeleteUserButton">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Style.Triggers>
<DataTrigger Binding="{Binding UserId, Converter={StaticResource converter}}" Value="False">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<Button.Content>
<Image Source="/Sinergi;component/Images/Trash-can-icon16.png" />
</Button.Content>
</Button>
</DataTemplate>
Converter:
class Converter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)value == Identity.This.OnBehalfOfUser.UserId;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
I have TreeView in my WPF window. The TreeViews DataContext is bound to a XDocument.
I am in search for setting the selectedItem from code behind. As the nodes returned by for example .ItemContainerGenerator.Items are of type System.Xml.Linq.XElement and not System.Windows.Controls.TreeViewItem I can not just set isSelected :-(
Does anybody have an idea how to solve this?
EDIT:
XAML Code:
<Window.Resources>
<DataTemplate x:Key="AttributeTemplate">
<StackPanel Orientation="Horizontal"
Margin="3,0,0,0"
HorizontalAlignment="Center">
<TextBlock Text="{Binding Path=Name}"
Foreground="{StaticResource xmAttributeBrush}" FontFamily="Consolas" FontSize="8pt" />
<TextBlock Text="=""
Foreground="{StaticResource xmlMarkBrush}" FontFamily="Consolas" FontSize="8pt" />
<TextBlock Text="{Binding Path=Value, Mode=TwoWay}"
Foreground="{StaticResource xmlValueBrush}" FontFamily="Consolas" FontSize="8pt" />
<TextBlock Text="""
Foreground="{StaticResource xmlMarkBrush}" FontFamily="Consolas" FontSize="8pt" />
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate x:Key="NodeTemplate">
<StackPanel Orientation="Horizontal" Focusable="False">
<TextBlock x:Name="tbName" Text="Root" FontFamily="Consolas" FontSize="8pt" />
<ItemsControl
ItemTemplate="{StaticResource AttributeTemplate}" HorizontalAlignment="Center"
ItemsSource="{Binding Converter={StaticResource AttrConverter}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</StackPanel>
<HierarchicalDataTemplate.ItemsSource>
<Binding Path="Elements" />
</HierarchicalDataTemplate.ItemsSource>
<HierarchicalDataTemplate.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=NodeType}" Value="Element" />
<Condition Binding="{Binding Path=FirstNode.FirstNode.NodeType}" Value="Text" />
</MultiDataTrigger.Conditions>
<Setter TargetName="tbName" Property="Text" >
<Setter.Value>
<Binding Path="Name" />
</Setter.Value>
</Setter>
<Setter TargetName="tbName" Property="ToolTip">
<Setter.Value>
<MultiBinding Converter="{StaticResource MultiString2StringKonverter}">
<Binding Path="FirstNode.Value" />
<Binding Path="FirstNode.NextNode.Value" />
</MultiBinding>
</Setter.Value>
</Setter>
</MultiDataTrigger>
<DataTrigger Binding="{Binding Path=NodeType}" Value="Element">
<Setter TargetName="tbName" Property="Text" Value="{Binding Path=Name}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=FirstNode.NodeType}" Value="Text">
<Setter TargetName="tbName" Property="Text">
<Setter.Value>
<MultiBinding StringFormat="{}{0} = {1}">
<Binding Path="Name"/>
<Binding Path="FirstNode.Value"/>
</MultiBinding>
</Setter.Value>
</Setter>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
</Window.Resources>
<TreeView x:Name="LizenzAnsicht"
ItemsSource="{Binding Path=Root.Elements, UpdateSourceTrigger=PropertyChanged}"
ItemTemplate="{StaticResource ResourceKey=NodeTemplate}"
/>
Code Behind:
...
LizenzAnsicht.DataContext = XDocument.Load(<Path to XML-File>);
After sleeping a weekend about this I found an answer myself. There were some more problems as only the return type.
The solution is, to get the matching ItemContainer for the Item. This Container is of type TreeViewItem, where I can set isSelected an the like.
As an example is most of the time better for understanding, I give one. The following function will search for a XML- node in a TreeView- object and return a TreeViewItem, that contains the matching Node. If no Match is found null is returned.
The matching node is expanded.
The XML is handled in a System.Xml.linq.XDocument which is bound to the System.Windows.Controls.TreeView DataContext.
When calling the function The TreeView is set as ic.
public static TreeViewItem SearchNodeInTreeView(ItemsControl ic, XElement NodeDescription, string indent = "")
{
TreeViewItem ret = null;
foreach (object o in ic.Items)
{
if (o is XElement)
{
var x = ic.ItemContainerGenerator.ContainerFromItem(o);
if (XNode.DeepEquals((o as XElement), NodeDescription))
{
ret = x as TreeViewItem;
}
else if ((o as XElement).HasElements){
if (x != null)
{
bool expanded = (x as TreeViewItem).IsExpanded;
(x as TreeViewItem).IsExpanded = true;
(x as TreeViewItem).UpdateLayout();
ret = SearchNodeInTreeView (x as TreeViewItem, NodeDescription, indent + " ");
if (ret == null)
{
(x as TreeViewItem).IsExpanded = expanded;
(x as TreeViewItem).UpdateLayout();
}
}
}
}
if (ret != null)
{
break;
}
}
return ret;
}
I hope I can help anybody with this.
I'm trying to implement what should be simple textbox validation for a WPF application, but I'm having some problems.
I used this guide: http://www.codeproject.com/Tips/690130/Simple-Validation-in-WPF
My textbox in MainWindow.xaml:
<TextBox x:Name="textbox1" HorizontalAlignment="Left" Height="23"
Margin="93,111,0,0" TextWrapping="Wrap" VerticalAlignment="Top"
Width="120" Style="{StaticResource textBoxInError}"
Validation.ErrorTemplate="{StaticResource validationErrorTemplate}">
<TextBox.Text>
<Binding Path="Name" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:NameValidator/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
My NameValidator Class in MainWindow.xaml.cs:
public class NameValidator : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
if (value == null)
return new ValidationResult(false, "value cannot be empty.");
else
{
if (value.ToString().Length > 3)
return new ValidationResult(false, "Name cannot be more than 3 characters long.");
}
return ValidationResult.ValidResult;
}
}
My Static Resources in App.xaml:
<ControlTemplate x:Key="validationErrorTemplate">
<DockPanel>
<TextBlock Foreground="Red" DockPanel.Dock="Top">!</TextBlock>
<AdornedElementPlaceholder x:Name="ErrorAdorner"></AdornedElementPlaceholder>
</DockPanel>
</ControlTemplate>
<Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
I can run the application without any errors, but the validation is never triggered.
Using what you posted, it works fine for me, it produces the red "!" above the textbox. However, I DID remember to set my DataContext, ie.
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
Without this, it won't work.