How can I binding a listboxitem content in a muiltbinding? - wpf

I am doing my first project using wpf. I really need your help and suggestion. Thanks in advance.
I am building a screen, which has two listboxes A & B. Listbox A is binding to items load from database. listbox B is binding to a statics list. When user selects an item in listbox A, the associate item will be highlighted in listbox B. I am doing it by using MultiBinding in a listboxtitem. I'd like to pass selected data object and listboxitem content. The Convert() will take these two variables and check them. If match, it will return true to IsSelected property of item and highlight it. But it seems item content (statics string) can't be passed to converter(). What should I do?
see my xaml code:
<ListBox Name="AbsenseCode" ItemsSource="{Binding absenseCodeItems}" Grid.Row="1" Grid.Column="1" Grid.RowSpan="5" Margin="20,0,20,5" >
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected">
<Setter.Value>
<MultiBinding Converter="{StaticResource IsItemSelected}">
<Binding ElementName="FilterListbox" Path="SelectedItem"/>
<Binding Path="Content"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
when debugging, the second binging variable, content of item, is "{DependencyProperty.UnsetValue}".

this is a few months old, but I had a similar situation.
First, build a iMultiValueConveter that will compare two values and return the result to a string. "true" or "false" or "yes" or "no".
then in the XAML, define a style that can be triggered by the converter return. In this, ListBoxB has an ellipse where the fill will be green if the values match, red if they dont:
<Window.Resources>
<myNamespace:IsEqualConverter x:Key="IsEqualConverter" />
</Window.Resources>
<ListBox Name="ListBoxA" ItemsSource="{Binding}">
</ListBox>
<ListBox Name="ListBoxB" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<StackPanel>
<StackPanel.Resources>
<!-- style triggered by converter result -->
<Style TargetType="Ellipse" x:Key="EllipseStyle">
<Setter Property="Fill" Value="#E5E5E5" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=result,Path=Text}" Value="true">
<Setter Property="Fill" Value="Green" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=result,Path=Text}" Value="false">
<Setter Property="Fill" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<Ellipse Height="50" Width="50" Style="{StaticResoure EllipseStyle}" />
<TextBlock x:Name="value0" Text="{Binding ElementName=ListBoxA,Path=SelectedItem.(Whatever),Mode=OneWay}" />
<TextBlock x:Name="value1" Text="{Data Binding you want to compare to}" VerticalAlignment="Center" HorizontalAlignment="Left" />
<TextBlock x:Name="result">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource IsEqualConverter}">
<Binding ElementName="value0" Path="Text" />
<Binding ElementName="value1" Path="Text" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</ListBox.ItemTemplate>
</ListBox>

Related

Relay Commands Command Parameter

I am trying to work with the MVVM principles within a small WPF project using C#.
I have a ListBox that is populated with CheckBoxes created through binding back to the ViewModel. I also have a command bound to the CheckBoxes and wish to pass the CheckBoxes Content as a CommandParameter. I was looking for something like this:
<Binding ElementName="" Path="Content"/>
Unfortunately, because the CheckBoxes are created through a binding I don’t have the element name.
The code for ListBox / ListBoxItem Style is this:
<Style x:Key="CheckBoxListStyle" TargetType="{x:Type ListBox}">
<Setter Property="SelectionMode" Value="Multiple"></Setter>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListBoxItem}" >
<Setter Property="Margin" Value="2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<CheckBox Command="{Binding SelectedItemCommand, Mode=OneWay, Source={StaticResource comd}}">
<CheckBox.CommandParameter>
<MultiBinding Converter="{StaticResource cv}">
<Binding ElementName="" Path="Content"/>
<Binding ElementName="" Path="IsChecked"/>
</MultiBinding>
</CheckBox.CommandParameter>
<ContentPresenter></ContentPresenter>
</CheckBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
And its implementation is:
<ListBox Grid.Row="1" Style="{StaticResource CheckBoxListStyle}" Name="lstProducts" ItemsSource="{Binding stampInfo, Mode=OneWay, Source={StaticResource vmStamp}}"
DisplayMemberPath="Country" >
</ListBox>
Ultimately my goal is to be able to display the text Contents (Countries in this case) of all the selected items in a text box were each country is separated by a comma. The only thing I am currently missing is the Country.
Do not create a ControlTemplate for ListBoxItem when you really want to display your data items differently, use a DataTemplate instead, that is exactly its purpose. See Data Templating Overview.
Remove the DisplayMemberPath from the ListBox, as you cannot use both use a path and a custom DataTemplate at the same time. You would only set this path, if there was no DataTemplate, but you wanted to specify a concrete property or property path to display.
<ListBox Grid.Row="1"
Style="{StaticResource CheckBoxListStyle}" Name="lstProducts"
ItemsSource="{Binding stampInfo, Mode=OneWay, Source={StaticResource vmStamp}}"/>
Replace the ControlTemplate with a DataTemplate as ItemTemplate. Then bind the Content and CommandParameter to the property Country. The data context is automatically set to the corresponding item in the bound collection of data items. The IsChecked property can be bound using a RelativeSource, which is the CheckBox itself.
<Style x:Key="CheckBoxListStyle" TargetType="{x:Type ListBox}">
<Setter Property="SelectionMode" Value="Multiple"></Setter>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListBoxItem}" >
<Setter Property="Margin" Value="2" />
</Style>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<CheckBox Content="{Binding Country}"
Command="{Binding SelectedItemCommand, Mode=OneWay, Source={StaticResource comd}}">
<CheckBox.CommandParameter>
<MultiBinding Converter="{StaticResource cv}">
<Binding Path="Country"/>
<Binding Path="IsChecked" RelativeSource="{RelativeSource Self}"/>
</MultiBinding>
</CheckBox.CommandParameter>
</CheckBox>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
A different option would be to create data items with a property that can be bound to the IsChecked property of CheckBox. Then you could act in the setter of the data item or on e.g. a button click that executes a command, which filters the bound collection in your view model for checked items.

WPF Error Validation doesn't show ErrorTemplate

I have a window like
<Window.Resources>
<ControlTemplate x:Key="ValidationTemplate">
<DockPanel>
<TextBlock Foreground="Red" FontSize="20">!</TextBlock>
<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>
</Window.Resources>
<TextBox x:Name="SupportFolder"
Validation.ErrorTemplate="{StaticResource ValidationTemplate}"
Style="{StaticResource TextBoxInError}"
Padding="2">
<TextBox.Text>
<Binding Path="Preferences.SupportFolder" Mode="TwoWay" UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<local:FolderExistsValidationRule ValidationStep="RawProposedValue"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
So my impression is that when the input for the TextBox is in error that there will be a small red exclamation mark to the left of the TextBox and because of the Style triggers there will be a ToolTip displaying the error. The ToolTip works but I don't see the exclamation point indicating the error. I took this code from the Microsoft Samples but I cannot for the life of me see where I have made a mistake. The sample works (Data Binding -> BindingValidation) but I cannot seem to reproduce the code. Would someone act as a second pair of eyes and see what I am doing wrong?
You should dock the TextBlock in the error template to the left or right:
<TextBlock Foreground="Red" FontSize="20" DockPanel.Dock="Right">!</TextBlock>
Using your current markup, the TextBlock will end up "under" the adorned element.

wpf DataGrid change wrapping on cells

I have a WPF DataGrid with AutoGenerate Columns. I have been able to override the column headers using code and also force wrapping on the column headers when I shrink the columns. When I try to force text wrapping on the cells my binding breaks... it shows the same value in every column.
Here is the XAML I am using to format
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Text="{Binding}"></TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.CellStyle>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Text="{Binding}"></TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.ColumnHeaderStyle>
Again, the ColumnHeaderStyle works fine, but the CellStyle is not working.
Suggestions?
Update:
Column headers are set as follows:
if (e.Column.Header.ToString() == "Product_Description")
e.Column.Header = "Product";
if (e.Column.Header.ToString() == "Original_Gross_Weight")
e.Column.Header = "Orig. Net Wt.";
The wrapping of the headers works well. Just the wrapping of the content does not work.
On the binding it seems that once one replaces the DataGridCell data style, the full object for for the row is placed into the content presenter instead of the current property of the column.
It appears you override AutoGeneratingColumn so why not simply turn of auto generate and define the columns by hand?
Here is a working version where the text is wrapped for the data:
<Window.Resources>
<model:People x:Key="People">
<model:Person First="Joe" Last="Smith" Phone="303-555 5555" />
<model:Person First="Mary" Last="Johnson" Phone="720-555 5555" />
<model:Person First="Frank" Last="Wright" Phone="202-555 5555" />
</model:People>
</Window.Resources>
<DataGrid AutoGenerateColumns="False"
ItemsSource="{StaticResource People}">
<DataGrid.Columns>
<DataGridTextColumn Header="First" Binding="{Binding First}" />
<DataGridTextColumn Header="The Name" Binding="{Binding Last}" />
<DataGridTextColumn Header="Phone Number" Binding="{Binding Phone}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping"
Value="Wrap" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
Result

Getting an error in searching data from dataGrid in wpf

I have tried to search in a dataGrid using the following XAML
<DataGrid x:Name="grid" local:DataGridTextSearch.SearchValue="{Binding ElementName=SearchBox, Path=Text, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding TestData}" >
<DataGrid.Resources>
<local:SearchValueConverter x:Key="SearchValueConverter" />
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="local:DataGridTextSearch.IsTextMatch">
<Setter.Value>
<MultiBinding Converter="{StaticResource SearchValueConverter}">
<Binding RelativeSource="{RelativeSource Self}" Path="DataContext.MyProperty" />
<Binding RelativeSource="{RelativeSource Self}" Path="(local:DataGridTextSearch.SearchValue)" />
</MultiBinding>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="local:DataGridTextSearch.IsTextMatch" Value="True">
<Setter Property="IsSelected" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
</DataGrid>
I got it from here
But it gives me an error that DataGrid is not defined. So I changed <DataGrid> to <my:DataGrid>
Now it gives me the following error on line no 1 :
The attachable property SearchValue was not found in type DataGridTextSearch.
And Another Error on Line no. 3 :
The type local:SearchValueConverter was not found. Verify that you are not missing an assembly reference and that all reference assemblies have been built.
The XAML of my window looks like :
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="532" xmlns:my="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit">
<Grid>
<TextBox Height="29" Margin="104,22,147,0" Name="txt" VerticalAlignment="Top" AutoWordSelection="True" />
<my:DataGrid x:Name="grid" local:DataGridTextSearch.SearchValue="{Binding ElementName=SearchBox, Path=Text, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding TestData}" >
<my:DataGrid.Resources>
<local:SearchValueConverter x:Key="SearchValueConverter" />
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="local:DataGridTextSearch.IsTextMatch">
<Setter.Value>
<MultiBinding Converter="{StaticResource SearchValueConverter}">
<Binding RelativeSource="{RelativeSource Self}" Path="DataContext.MyProperty" />
<Binding RelativeSource="{RelativeSource Self}" Path="(local:DataGridTextSearch.SearchValue)" />
</MultiBinding>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="local:DataGridTextSearch.IsTextMatch" Value="True">
<Setter Property="IsSelected" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
</my:DataGrid.Resources>
</my:DataGrid>
</Grid>
</Window>
You have messed up with namespaces, references and (i guess) Resharper.
First of all - no need to specify namespace for DataGrid - it is standard control, so if it is not recognized - possibly you really miss some assemblies in your project's references.
Then - check your namespace - I guess that x:Class="Window1" is wrong, because usually there is project namespace prefix there.
Third, before using any namespace, including "local", you need to define it first, like xmlns:local="clr-namespace:your namespace goes here", so local:DataGridTextSearch and similar expressions won't work before you do so.
And finally, build your solution before proceeding to fixing XAML warnings - usually, if you did everything right, they vanish after build.
What I recommend - create a new empty WPF Application project, copy there ALL the code (including codebehind) from the post you mentioned and check if your problems persist.

How do you set WPF Hyperlink text via XAML

I have a button that has its content (text) set dynamically via a style against a backing property as below.
<Button>
<Button.Style>
<Style>
<Setter Property="Button.Content" Value="Advanced Search" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsAdvancedSearch}" Value="True">
<Setter Property="Button.Content" Value="Standard Search" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
I need to change this button to display just a hyperlink with the same dynamic text. Like this:
<Button>
<Button.Template>
<ControlTemplate>
<TextBlock>
<Hyperlink>
Standard Search
</Hyperlink>
</TextBlock>
</ControlTemplate>
</Button.Template>
</Button>
Is there a way to set the hyperlink's text (inline or some other tag) dynamically via a style still?
I haven't been able to get access to it via XAML. I got it working with a normal binding on a textblock inside the hyperlink but that is creating a redundant property on the viewmodel really.
You can embed another TextBlock inside your Hyperlink and bind it:
<TextBlock>
<Hyperlink>
<TextBlock Text="{Binding LinkText}" />
</Hyperlink>
</TextBlock>
Solution was to simply apply the style to an inner Textblock.
<Button x:Name="SwitchSearchType">
<Button.Template>
<ControlTemplate>
<TextBlock>
<Hyperlink>
<Hyperlink.Inlines>
<TextBlock>
<TextBlock.Style>
<Style>
<Setter Property="TextBlock.Text" Value="Advanced Search" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsAdvancedSearch}" Value="True">
<Setter Property="TextBlock.Text" Value="Standard Search" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Hyperlink.Inlines>
</Hyperlink>
</TextBlock>
</ControlTemplate>
</Button.Template>
</Button>
<GridViewColumn Header="{x:Static lang:Lang.wName}" CellTemplate="{StaticResource Template_DebitAccount}" />
And use into skin file
<DataTemplate x:Key="Template_DebitAccount">
<Border Padding="3" >
<TextBlock Text="{Binding wDebitAccountName}" Style="{StaticResource TextStyle_Path}">
<TextBlock.InputBindings>
<MouseBinding MouseAction="LeftClick" Command="{Binding DataContext.Base_PopPageCommand , RelativeSource={RelativeSource AncestorType=ListView}}" >
<MouseBinding.CommandParameter>
<MultiBinding Converter="{StaticResource MultiValueBindingConverter}">
<Binding Source="AgentSummary" />
<Binding Path="Link_Text"/>
</MultiBinding>
</MouseBinding.CommandParameter>
</MouseBinding>
</TextBlock.InputBindings>
</TextBlock>
</Border>
</DataTemplate>
Given:
<Hyperlink x:Name="uriEmailAddress" Click="Hyperlink_Click"></Hyperlink>
Code:
string e = Properties.Settings.Default.Email;
uriEmailAddress.NavigateUri = new Uri("mailto:" + e);
InlineCollection ic = uriEmailAddress.Inlines;
ic.Add(new Run(e));

Resources