Comma-separated text boxes in listbox - wpf

given a list of business objects with a fixed number of properties (for example List of person, and person with properties FirstName, LastName, City, Department
I would like to show each person in a listboxitem, and was able to define a datatemplate that does the display.
Now the question: I do NOT want to display a gridlike structure, but would like to see only filled textboxes, and they should be separated by commas:
"Karl, Miller, Chicago, Legal" when all fields are filled, but
"Harry, Manning" when city and department is empty and
"Maria, IT" when lastname and city is not set.
Which is the way to choose for this task?
Regards

Use Trigger.
<ListBox ItemsSource="{Binding MyObjects}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<StackPanel.Resources>
<Style TargetType="TextBlock" BasedOn="{DynamicResource x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="TextBlock.Text" Value="">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
<Trigger Property="TextBlock.Text" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<TextBlock Text="{Binding FirstName}" />
<TextBlock Text="{Binding LastName}" />
<TextBlock Text="{Binding City}" />
<TextBlock Text="{Binding Department}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This solution does not have commas, but I need to know exactly what you want it to look like before I can really suggest that.
Alternatively, use MultiBinding with an IMultiValueConverter.

Solved this like this:
In Xaml I have this Structure:
...
<local:CaptionedTextBox Caption="{x:Static p:Resources.EMail}"
Text="{Binding EMail.Value}"
Visibility="{Binding EMail.Value,
Converter={StaticResource LengthToVisibilityConverter}}" />
<local:SeparatorBox Visibility="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource LastFilledToVisibilityConverter}}" />
<local:CaptionedTextBox Caption="{x:Static p:Resources.Mobile}"
Text="{Binding Mobile.Value,
UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding Mobile.Value,
Converter={StaticResource LengthToVisibilityConverter}}" />
<local:SeparatorBox Tag="HIDE" Visibility="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource LastFilledToVisibilityConverter}}" />
So, basically I have list of alternating Text and Separator elements.
The last element in the list is a Separator with TAG=HIDE.
In the Converter I only set TAG for not necessary separators to HIDE and finally set the visibility for all separators regarding the TAG.

Related

How to apply style to Listbox Item based on Property value in WPF

<ListBox ItemsSource="{Binding Customers}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="1" x:Name="BackgroundGrid">
<TextBlock Text="{Binding CustomerName}" />
<TextBlock Text="{Binding CustomerId}" />
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding CustomerId}" Value="9-3453"><Setter TargetName="BackgroundGrid" Property="Background" Value="Red"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In the above code, I need to provide DataTrigger's value based on property value instead of hard code the value.
Thanks in advance,
Dinesh
Supposed your DataTrigger's value based on property CustomerNumber. Then you add another property for binding, like this:
public bool CustomerIdMatchs
{
get{
return CustomerNumber==CustomerId;
}
}
Then you bind CustomerIdMatchs instead of CustomerId, like this:
<DataTrigger Binding="{Binding CustomerIdMatchs}" Value="true">
<Setter TargetName="BackgroundGrid" Property="Background" Value="Red"/>
</DataTrigger>

set default string name on combobox

I am using a Combobox whose ItemSource is ObservableCollection(i.e. ConversationList) of type .
<ComboBox x:Name="ConvId"
Grid.Row="2"
Width="75"
Height="23"
Margin="6,94,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemsSource="{Binding ConversationList,
UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedId,
Mode=TwoWay}">
My requirement is : when there is no int value in Collection then Combobox Left side should display 'ConvId' which is a string. Fig. is shown below
I have workaround i.e to Convert collection from int to string and put 'ConvId' on 0th location and mark SelectedIndex= 0. but its not we want.
Do I have to use some Custom control for this. Is there any to acheive this in XAML.
This Stack thread seems to do what you want cleanly with a Converter.
How to display default text "--Select Team --" in combo box on pageload in WPF?
The answer I am referring to starts with this:
<Grid>
<ComboBox
x:Name="comboBox1"
ItemsSource="{Binding MyItemSource}" />
<TextBlock
Visibility="{Binding SelectedItem, ElementName=comboBox1, Converter={StaticResource NullToVisibilityConverter}}"
IsHitTestVisible="False"
Text="... Select Team ..." />
</Grid>
Have below textblock below your combobox and make sure that both combobox and textblock are overlapping each other(i.e. they should be in same grid row)
<TextBlock Text="ConvID"
IsHitTestVisible="False">
<TextBlock.Style>
<Style
TargetType="TextBlock">
<Setter
Property="Visibility"
Value="Collapsed" />
<Style.Triggers>
<DataTrigger
Binding="{Binding ConversationList.Count}"
Value="0">
<Setter
Property="Visibility"
Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Hope this helps.
Have a look at the WatermarkService by John Myczek. You can have it display a default string if no item is selected in the ComboBox. There's some issues with ComboBox in the answer I linked to, but if you look further down there's a fix for that.

Controls with TemplateSelector property

Now I have ListView and in one column has:
<GridViewColumn CellTemplateSelector="{StaticResource messagerEditorTemplateSelector}"/>
And everything is fine: cell is filled with content based on item. But now I want to place in this cell 2 controls: for one template must be selected based on binding and other is control with name, say TimeRangeView. But I cannot understand how it can be implemented? So I must have code like:
<GridViewColumn>
<DataTemplate>
<StackPanel>
<SomeControlWhichSupportTemplateSelector ... />
<views:TimeRangeView ... />
</StackPanel>
</DataTemplate>
</GridViewColumn>`
Which control should I use for template? I've found only listbox but it must be bound to collection. Of course, I could bind like:
<ListBox ItemsSource="{Binding Converter=ItemToCollectionConverter}" />
but it doesn't look elegant. May be there is another way to do it?
You can use a ContentControl and set its ContentTemplateSelector property:
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<ContentControl ContentTemplateSelector="{StaticResource messagerEditorTemplateSelector}" />
<views:TimeRangeView ... />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Note that for Binding to work within your ContentControl, you will have to set the Content property to whichever object is used in the Bindings of the DataTemplate returned by your selector.
So that's for option 1.
You can also just use DataTriggers:
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<ContentControl Content="{Binding MyBoundObject}">
<ContentControl.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=MyBoundObject.ABooleanProperty}" Value="True">
<Setter Property="ContentControl.ContentTemplate" Value="{StaticResource myFirstTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=MyBoundObject.ABooleanProperty}" Value="False">
<Setter Property="ContentControl.ContentTemplate" Value="{StaticResource mySecondTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
<views:TimeRangeView ... />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Which is what i do and it works like a charm =)

XAML trigger template to set visibilty based on another element

I have several StackPanels that change visibility based on ToggleButtons. The code below works if I replace Tag with btn1 on the DataTrigger-lines.
How do I use the value of the Tag property?
<Window x:Class="MyTestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestApp">
<Window.Resources>
<Style x:Key="panelStyle" TargetType="{x:Type StackPanel}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Tag, Path=IsChecked}" Value="False">
<Setter Property="StackPanel.Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=Tag, Path=IsChecked}" Value="True">
<Setter Property="StackPanel.Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<WrapPanel>
<ToggleButton Content="One" Name="btn1" />
<ToggleButton Content="Two" Name="btn2" />
<StackPanel Style="{StaticResource panelStyle}" Tag="{Binding btn1}">
<Label Content="Data to panel 1" />
</StackPanel>
<StackPanel Style="{StaticResource panelStyle}" Tag="{Binding btn2}">
<Label Content="Data to panel 2" />
</StackPanel>
</WrapPanel>
</Window>
This question is very similar, but I'm missing details on how to pass an element name.
XAML - Generic textbox stylewith triggers / parameters?
Your bindings are incorrect.
In your DataTemplate the bindings should be:
<DataTrigger Binding="{Binding Path=Tag.IsChecked, RelativeSource={RelativeSource Self}}" Value="False">
<Setter Property="StackPanel.Visibility" Value="Collapsed" />
</DataTrigger>
Here the RelativeSource with a mode of Self tells the binding engine that the object to bind against is object to which the style is being applied (e.g. your StackPanel). The PropertyPath of Tag.IsChecked tells the binding engine to look for a property called IsChecked from the object stored in Tag.
Finally the bindings in your StackPanel should be:
<StackPanel Style="{StaticResource panelStyle}" Tag="{Binding ElementName=btn1}">
<Label Content="Data to panel 1" />
</StackPanel>
Here ElementName creates a binding to another element in the logical tree. If you do not explicitly assign to any properties in a Binding as in your original example:
Tag="{Binding btn1}"
The value specified is assigned to the Path property. So this would be the same as:
Tag="{Binding Path=btn1}"
Also note, that using Tag is not considered best practice since it's type is of object and its use is unrestricted, and hence can take on any number of different meanings throughout your project (which often makes it difficult to understand, especially when used in Templates that are located far away from their actual use).
Hope this helps!
Use Converter: set the visibility of StackPanel:
<StackPanel Visivility="{Binding IsChecked, ElementName=btn1, Converter={StaticResource BooleanToVisibilityConverter}}">
...
</StackPanel>

Show "No record found" message on a WPF DataGrid when it's empty

If there is no record available, I want to add a TextBlock on data grid, below the header, showing the message "No Record Found."
Consider the attached image for reference.
Its been a long time since the question has been posted. But I thought this might be useful to someone else.
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>
<DataGrid Name="dgProjects" ItemsSource="{Binding Projects}" AutoGenerateColumns="True" />
<TextBlock Text="Employee has no projects" Visibility="{Binding Items.IsEmpty, Converter={StaticResource BooleanToVisibilityConverter}, ElementName=dgProjects}" />
For simplicity purpose I have set AutoGenerateColumns="True". Please define the columns. This way when a empty datasource is bound, the column names will be shown along with 'Empty row' message.
Finally I am able to findout the way.
When the grid in empty, add a default row on grid
Create a RowDetailTemplate which contain a text block with a message "No Record Found"
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="No Record Found" Width="400"></TextBlock>
</StackPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
Set the style on datagrid
<DataGrid.Style>
<Style TargetType="DataGrid">
<Setter Property="RowDetailsVisibilityMode" Value="Collapsed"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.IsRecordExists,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type local:MainWindow}}}" Value="false">
<Setter Property="RowHeight" Value="0"></Setter>
<Setter Property="RowDetailsVisibilityMode" Value="Visible"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Style>
By default (record available on datagrid) row detail template will be collapsed.
DataTrigger that checks CLR poperty, if it is false then show the row detail template.
The reason for setting the rowheight as 0 to hide the default row which we haved added on 1st step.
Add the grid inside the stackpanel
Place below border code next to datagrid
<Border HorizontalAlignment="Stretch" VerticalAlignment="Center"
BorderThickness="1,0,1,1" BorderBrush="Black" Height="35">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding YourListName.Count}" Value="0">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="No record fount" HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
It will show/hide based on your collection/list count.
I find that it is easy to center a text block over the grid and set its visibility based on the number of rows. I am usually using MVVM and will bind the visibility to a View Model property:
<Grid>
<toolkit:DataGrid>
<toolkit:DataGrid.Columns>
.
.
.
</toolkit:DataGrid.Columns>
</toolkit:DataGrid>
<TextBlock Text="No Records Found" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="{Binding EmptyMessageVisibility, Mode=OneWay, FallbackValue=Visible}" />
</Grid>

Resources