GridViewColumn and header not set inside ListView - wpf

I have a list view that shows some logs (of type LogItem). The ListView.View is set to a GridView in XAML. The GridView has two columns where the data templates for each column is set to a textbox. The first column binds to a DateTime property of the LogItem, so the width of the first column could be set to Auto (preferred) or even a fixed width i required.
The second gridviewcolumn is binding to a log message string, so it could have any width.
My question is, how can I make sure all text is visible in the listview such that I get a scrollbar in the listview if the log message width is larger than the listview's width, and that all the text is visible?
At the moment the size of the second column seems to be set by the first item added to the collection that the ListView binds to, then the width never updates. So typically I just see a small part of the log messages and the second column header does not even span the complet ListView width.
I have looked around for a solution to the problem. There seems to be more people asking the same question, but so far none of the answers I have found has solved the problem. I hope someone could help to guide me in the right direction, what have I missed.
XAML code below
<ListView Grid.Row="1"
HorizontalAlignment="Stretch"
IsSynchronizedWithCurrentItem="False"
ItemsSource="{Binding LogItems}"
x:Name="LogListView"
FontSize="14">
<!--:GridViewColumnResize.Enabled="True" -->
<ListView.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Gray" Offset="1"/>
<GradientStop Color="White"/>
</LinearGradientBrush>
</ListView.Background>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Background" Value="{Binding BackColor}"/>
<Setter Property="TextElement.Foreground" Value="White"/>
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true" />
</MultiTrigger.Conditions>
<Setter Property="Background" Value="Gold" />
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="TextElement.Foreground" Value="Black"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="true" />
</MultiTrigger.Conditions>
<Setter Property="Background" Value="Gold" />
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="TextElement.Foreground" Value="Black"/>
</MultiTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="{Binding TimeText}" Width="Auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="TimeLabel" Text="{Binding CreationTime, StringFormat='{}{0:hh:mm:ss.fff}'}" Grid.Column="0"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="{Binding MessageText}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Message}" Grid.Column="1"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Edit:
The full xaml code
pastebin.com/24HgAcML

Related

Editable WPF ListBox contains textbox

I want to have dynamic editable listbox in wpf application. I am using textbox inside listbox, and I am binding Observable collection to that listbox. Now, I want to make textbox editable on mouse click. So, user can make change to textbox and save new textbox text.
<ListBox Name="ListTwo" ItemsSource="{Binding CollectionUrl, Mode=TwoWay}" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Name="TextBoxList" Text="{Binding Path=urlString}" IsEnabled="False" >
</TextBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You should use IsReadOnly property. In the trigger you should check IsFocused property.
In the following example, I changed foreground color to indicate which TextBox is in the edit mode.
Example:
<ListBox Name="ListTwo" ItemsSource="{Binding CollectionUrl, Mode=TwoWay}" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Name="TextBoxList" Text="{Binding Path=urlString}" MinWidth="100">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Foreground" Value="Green"/>
<Setter Property="IsReadOnly" Value="False" />
</Trigger>
<Trigger Property="IsFocused" Value="False">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="IsReadOnly" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If you want to allow users save changes after edit value in the TextBox, you can add button and show in the actual editing row:
<ListBox Name="ListTwo" ItemsSource="{Binding CollectionUrl, Mode=TwoWay}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<TextBox Name="TextBoxList" Text="{Binding Path=urlString}" MinWidth="100">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Foreground" Value="Green"/>
<Setter Property="IsReadOnly" Value="False" />
</Trigger>
<Trigger Property="IsFocused" Value="False">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="IsReadOnly" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<Button Content="Save" Grid.Column="1" Command="{Binding SaveChanges}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=TextBoxList, Path=IsFocused}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My approach to accessing the text box (which was proving a nightmare) was to use your save button approach and in the Button's Button_Click function I used sender to retrieve the Button's parent, cast to (in your case) Grid. Then you can use that to access the Grid's Children with .Children[0] being your TextBox.
Bit of a Kluge because your code has to 'know' the type of the parent and the index of the child TextBox but these will not change. If necessary the purists can iterate through the Children to identify the required child explicitly.
Hope this helps someone.

How to bind to a control element within a ResourceDictionary

I'm trying to refer to an element in my WPF Control from within a ResourceDictionary on that control. Here's an example:
<UserControl.Resources>
<ResourceDictionary>
<Behaviors:GridViewInteractionModel x:Key="gridViewInteraction" GridView="{Binding ElementName=myGridView}"/>
</ResourceDictionary>
</UserControl.Resources>
...
<SomeGridView x:Name="myGridView"/>
The value of the GridView dependency property on the GridViewInteractionModel object should be the SomeGridView object called myGridView.
The above code doesn't work. {Binding ElementName=myGridView} does not bind the element (the SetValue function of GridViewInteractionModel never gets called).
The WPF runtime error is:
Cannot find source for binding with reference 'ElementName=myGridView'.
BindingExpression:(no path); DataItem=null; target element is
'GridViewInteractionModel' (HashCode=15238415); target property is
'GridView' (type 'SomeGridView')
Does anyone know how to get an element within the control to be bound to a property of a resource within the ResourceDictionary?
The only way I've found to get the property set is by manually setting it in the code-behind constructor like this, after InitializeComponent() has been called:
(Resources["gridViewInteraction"] as GridViewInteractionModel).GridView = FindName("myGridView") as SomeGridView;
But that's really ugly (and error-prone).
Thx.
Not entirely sure what you are trying to achieve here. This is an example of how I have dealt with a listview and it's child items:
<Style x:Key="ListViewStyle" TargetType="ListView">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFD3D1CF" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Opacity="0.7"/>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ListViewItemStyle" TargetType="ListViewItem">
<Setter Property="Background" Value="#FF494646"/>
<Setter Property="Foreground" Value="#FFFDFAFA"/>
<Setter Property="Height" Value="25"></Setter>
<Setter Property="Cursor" Value="Hand"></Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Black"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="Black"/>
</Trigger>
</Style.Triggers>
</Style>
<ListView x:Name="lvwSettingsYears" x:Uid="lvwSettingsYears" Margin="10,77,10,50"
Style="{DynamicResource ListViewStyle}"
ItemContainerStyle="{DynamicResource ListViewItemStyle}"
SelectionChanged="lvwYears_SelectionChanged">
<ListView.View>
<GridView>
<GridViewColumn x:Name="SettingsYearListviewID" x:Uid="SettingsYearListviewID" DisplayMemberBinding="{Binding id}" Width="0"/>
<GridViewColumn x:Name="SettingsYearListviewCode" x:Uid="SettingsYearListviewCode" Header="Code" DisplayMemberBinding="{Binding Code}" Width="100"/>
<GridViewColumn x:Name="SettingsYearListviewDesc" x:Uid="SettingsYearListviewDesc" Header="Description" DisplayMemberBinding="{Binding Description}" Width="300"/>
</GridView>
</ListView.View>
</ListView>
What prompted me to post this is in your error message it mentions
target property is
'GridView' (type 'SomeGridView')
I think it is looking for the control type.

style triggers for alternating rows don't always update when scrolling virtualized WPF Datagrid

There are a few questions similar to this on SO, I've read this one and another I can't find the link for, but none of them seem to have a solution that's applicable.
I have a DataGrid defined below, and it has various styles triggered on AlternationIndex being either 0 or 1. When I scroll upwards, sometimes the a given cell will flip from one colour to the other.
Do you know of any way to stop this from happening without turning off virtualization?
(I've taken the column definitions out to save space, I don't think they're important for this. All of the DataTriggers -always- work, it's just the alternation that I'm having issues with.)
<DataGrid
ItemsSource="{Binding Path=LogItems, Mode=OneWay}"
Grid.Row="1"
AlternationCount="2"
Name="logDataGrid"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">
<DataGrid.Resources>
<local:IsEntryExceptionConverter x:Key="isEntryExceptionConverter" />
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Border Name="DataGridCellBorder">
<ContentControl Content="{TemplateBinding Content}">
<ContentControl.ContentTemplate>
<DataTemplate>
<TextBlock Background="Transparent" TextWrapping="WrapWithOverflow" TextTrimming="CharacterEllipsis"
Height="auto" Width="auto" Text="{Binding Text}"/>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="DataGridCell.IsSelected" Value="True">
<Setter Property="TextBlock.Foreground" Value="Blue" />
</Trigger>
</Style.Triggers>
</Style>
<SolidColorBrush x:Key="ExceptionBrush" Color="OrangeRed" Opacity="0.5"/>
<SolidColorBrush x:Key="ErrorBrush" Color="Red" Opacity="0.5"/>
<SolidColorBrush x:Key="WarningBrush" Color="Orange" Opacity="0.5"/>
<SolidColorBrush x:Key="AlternatingRowBackground0" Color="AliceBlue" Opacity="0.5" />
<SolidColorBrush x:Key="AlternatingRowBackground1" Color="LightBlue" Opacity="0.5" />
</DataGrid.Resources>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<Trigger Property="AlternationIndex" Value="0">
<Setter Property="Background" Value="{StaticResource AlternatingRowBackground0}" />
</Trigger>
<Trigger Property="AlternationIndex" Value="1">
<Setter Property="Background" Value="{StaticResource AlternatingRowBackground1}" />
</Trigger>
<DataTrigger Binding="{Binding Path=Level}" Value="Warning">
<Setter Property="Background" Value="{StaticResource WarningBrush}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Message, Converter={StaticResource isEntryExceptionConverter}}" Value="True">
<Setter Property="Background" Value="{StaticResource ExceptionBrush}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Level}" Value="Error">
<Setter Property="Background" Value="{StaticResource ErrorBrush}" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>
That's the nature of virtualization. Only a set number of UI objects are actually rendered, and when you scroll you are changing the DataContext behind those objects.
So in your case, Rows are created and given a background color. When you scroll, the DataContext behind those rows change, so a data object might be in a row that was given Color A at a certain scroll position, or be in a row that was assigned color B at another scroll position.
Most of the time the alternating colors are only there to help identify what columns are in what row so it doesn't matter if they change, however if you want to maintain a consistent background color for the rows you will probably have to add something to the data object and base your background color off that property. That way when you scroll and the DataContext changes, the row color will also change.

Styling WPF Tree At All Levels

I have a tree in WPF that has no root node, but has defined TreeViewItems for separate categories. Each of these TreeViewItems have their ItemsSource bound to the display object. The data displays fine. Now I'm trying to make the tree appear like the Windows7 Explorer trees.
I've used some of the styling from this post: WPF TreeView: How to style selected items with rounded corners like in Explorer
Anyhow here is the TreeViewItem:
<TreeViewItem ItemsSource="{Binding Path=Configuration.CognosServers}"
IsExpanded="True"
Visibility="{Binding ItemsSource.Count, Converter={StaticResource ResourceKey=TreeViewItemVisibility1}, RelativeSource={RelativeSource Self}}">
<TreeViewItem.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True"/>
</Style>
</TreeViewItem.ItemContainerStyle>
<TreeViewItem.HeaderTemplate>
<DataTemplate>
<Border Margin="0,5,0,0">
<Border.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" Click="TreeItemMenu_RemoveClick" />
<MenuItem Header="Add Client..." Click="TreeItemMenu_AddNewClient" />
</ContextMenu>
</Border.ContextMenu>
<StackPanel Orientation="Horizontal">
<Image Source="/WPF;component/Images/server_chart.png"
Margin="0,0,5,0"/>
<TextBlock Text="Cognos Servers" />
</StackPanel>
</Border>
</DataTemplate>
</TreeViewItem.HeaderTemplate>
<TreeViewItem.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Namespaces}">
<Border>
<TextBlock Text="{Binding DisplayName}" PreviewMouseRightButtonDown="OnPreviewMouseRightButtonDown" />
</Border>
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource ResourceKey=TreeViewItemStyle1}">
<Setter Property="IsExpanded" Value="True"/>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Clients}">
<Border>
<TextBlock Text="{Binding DisplayName}" PreviewMouseRightButtonDown="OnPreviewMouseRightButtonDown" />
</Border>
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource ResourceKey=TreeViewItemStyle1}">
<Setter Property="IsExpanded" Value="True"/>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<Border>
<TextBlock Text="{Binding DisplayName}"
ContextMenu="{StaticResource ResourceKey=ContextMenuTreeItem}"
PreviewMouseRightButtonDown="OnPreviewMouseRightButtonDown" />
</Border>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
The style I'm apply to the TreeView appears like this:
<TreeView x:Name="TreeViewLoadedItems"
Grid.Row="1"
VerticalAlignment="Stretch"
ItemContainerStyle="{DynamicResource TreeViewItemStyle1}"
MouseDoubleClick="TreeViewItem_MouseDoubleClick"
SelectedItemChanged="TreeViewLoadedItems_SelectedItemChanged" >
Finally the style I'm applying is this:
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type TreeViewItem}">
<!-- Style for the selected item -->
<Setter Property="BorderThickness" Value="1"/>
<Style.Triggers>
<!-- Selected and has focus -->
<Trigger Property="IsSelected" Value="True">
<Setter Property="BorderBrush" Value="#7DA2CE"/>
</Trigger>
<!-- Mouse over -->
<!--<Trigger Property="helpers:TreeView_IsMouseDirectlyOverItem.IsMouseDirectlyOverItem" Value="True">
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFFAFBFD" Offset="0"/>
<GradientStop Color="#FFEBF3FD" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="BorderBrush" Value="#B8D6FB"/>
</Trigger>-->
<!-- Selected but does not have the focus -->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True"/>
<Condition Property="IsSelectionActive" Value="False"/>
</MultiTrigger.Conditions>
<Setter Property="BorderBrush" Value="#D9D9D9"/>
</MultiTrigger>
</Style.Triggers>
<Style.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="2"/>
</Style>
</Style.Resources>
</Style>
</Setter.Value>
</Setter>
</Style>
No Matter what I've done I can't seem to get each item to look the same when selected. Only the leaf nodes seem to be picking up the style. I always have trouble when there are HierarchialDataTemplates and I need to style things. What gives?!
In your style you set the ItemContainerStyle, why? That way the immediate TreeViewItem the style is applied to will not have the style, maybe that somehow even propagates down to the leaf; you should not need to set that property. Just place a style for TreeViewItems in the Resources of the TreeView and it will automatially apply to each item, move everything in the ItemContainerStyle up to the actual main style.

How do I style a DataGridHeader based on information from the DataGridColumn?

I have a DataGrid, and in that grid, some of the columns are marked Read only:
<DataGrid AutoGenerateColumns="False">
<DataGrid.Columns>
<!-- this column is read only -->
<DataGridTextColumn Header="Column A" Binding="{Binding Path=PropertyA}" IsReadOnly="True" />
<!-- this column is EDITABLE -->
<DataGridTextColumn Header="Name" Binding="{Binding Path=Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" IsReadOnly="False" />
<!-- this column is read only -->
<DataGridTextColumn Header="Column C" Binding="{Binding Path=PropertyC}" IsReadOnly="True" />
</DataGrid.Columns>
I want that "Name" column to be visually distinguishable by the header that it is editable, when the other two columns are not. I can't seeem to get to the IsReadOnly property of the DataGridColumn, though.
I'm effectively trying to do something like:
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader" >
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGridColumn}, Path=IsReadOnly}" Value="false">
<Setter Property="Background" Value="Azure" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.ColumnHeaderStyle>
From this question:
Binding Visible property of a DataGridColumn in WPF DataGrid, it appears that DataGridColumn isn't a framework element, so i can't find it using RelativeSource AncestorType=DataGridColumn. That poster says they used a static resource to find it, but doesn't explain what/how (and several answers there are questions of how the poster solved it)
This question: How to get DataGridColumnHeader from DataGridColumn?, looks like i could get to it from code, but i'd really like this to just be xaml and generic to apply to any data grid.
Is there something simple i'm overlooking?
Answering my own question, in case anyone else runs into this...
It turns out there isn't a lot you can do here from XAML, because of some of the things mentioned in the question. The simplest thing i could do from XAML was make the headers of the editable columns bold to differentiate them from the rest of the columns.
<DataGridTextColumn Header="Editable Column" Binding="{Binding Path=EditableProperty,Mode=TwoWay}" IsReadOnly="False" Width="150">
<DataGridTextColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="ToolTip" Value="You can modify the values of this column."/>
</Style>
</DataGridTextColumn.HeaderStyle>
</DataGridTextColumn>
You can do more complicated things, like:
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="False">
<Trigger.Setters>
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="{DynamicResource ControlLightColor}" Offset="0" />
<GradientStop Color="LightSteelBlue" Offset="1" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger.Setters>
</Trigger>
</Style.Triggers>
But you end up getting some really wierd behaviors, where the sort icon disappears, or other wierd things. If you want to change the column headers and make it look consistant, you pretty much have to restyle the whole datagrid and all of its headers through styles and templates.
DataGridColumnHeader has property Column, which has the property IsReadOnly
<Label Content="(read only)" DockPanel.Dock="Bottom">
<Label.Style>
<Style TargetType="Label">
<Setter Property="Visibility" Value="Collapsed"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGridColumnHeader},Path=Column.IsReadOnly}" Value="True">
<Setter Property="Visibility" Value="Visible"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
In your example you could do:
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader" >
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGridColumnHeader}, Path=Column.IsReadOnly}" Value="False">
<Setter Property="Background" Value="Azure" />
</DataTrigger>
</Style.Triggers>
</Style>

Resources