ListView Column Width Auto - c#

I wrote code in XAML - WPF Browser Application - Page , just one Listview and one button to add new data to the listview (from other file), I am trying to make the first column auto size itself when the button is pushed, I am using Visual Studio c# 2010.
I've used the following method in the code behind, but AutoResizeColumns won't be recognized and gives an error.
Unfortunately, none of the previously suggested solutions worked with me.
The Code Behind
public partial class Page1 : Page, INotifyPropertyChanged
{
public Page1()
{
InitializeComponent();
this.DataContext = new Page1Model();
}
private void TestListe_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
public event PropertyChangedEventHandler PropertyChanged;
private void Button1_Click(object sender, RoutedEventArgs e)
{
TestListe1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
}
}
XAML
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="3*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<ListView Name="TestListe1" Margin="68,22,421,8" FontSize="12" >
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Width="auto"> <GridViewColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="ST1" Margin="10,0,10,1"/>
</DataTemplate>
</GridViewColumn.HeaderTemplate>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding One}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
<Button Name="Button1" Grid.Row="1" Height="27" Width="95" Margin="262,24,444,74" Click="Button1_Click" />
</Grid>

AutoResizeColumns is from the namespace System.Windows.Forms. I'm not sure if that will work with WPF or not. But you can set the width of the column to NAN to make it resize
In your XAML if you name your GridView as follows:
<GridView x:Name="dataGridView">
Then you could resize all columns with this
foreach (GridViewColumn c in dataGridView.Columns)
{
c.Width = 0; //set it to no width
c.Width = double.NaN; //resize it automatically
}

Related

How to pass a GridViewColumnHeader object to a method?

I have a listview that pulls a bunch of computer numbers and information from a database and displays it. I have a little menu that will filter this listview. Currently, clicking the columns sorts the listview by ascending/descending, but I want this function to be triggered by radio buttons.
I'm basically trying to trigger the GridViewColumnHeader_Click method from the radiobutton method. Here is the GridViewColumnHeader_Click method:
private GridViewColumnHeader listViewSortCol = null;
private SortAdorner listViewSortAdorner = null;
private void GridViewColumnHeader_Click(object sender, RoutedEventArgs e)
{
GridViewColumnHeader column = (sender as GridViewColumnHeader);
string sortBy = column.Tag.ToString();
if (listViewSortCol != null)
{
AdornerLayer.GetAdornerLayer(listViewSortCol).Remove(listViewSortAdorner);
lstView.Items.SortDescriptions.Clear();
}
ListSortDirection newDir = ListSortDirection.Ascending;
if (listViewSortCol == column && listViewSortAdorner.Direction == newDir)
newDir = ListSortDirection.Descending;
listViewSortCol = column;
listViewSortAdorner = new SortAdorner(listViewSortCol, newDir);
AdornerLayer.GetAdornerLayer(listViewSortCol).Add(listViewSortAdorner);
lstView.Items.SortDescriptions.Add(new SortDescription(sortBy, newDir));
}
private void RbLocation_Checked(object sender, RoutedEventArgs e)
{
GridViewColumnHeader_Click(sender, RoutedEventArgs e);
}
<RadioButton x:Name="rbP2L" Content="P2L" MinWidth="40" Checked="RbP2L_Checked"/>
<RadioButton x:Name="rbAssy" Content="Assembly" Checked="RbAssy_Checked"/>
<RadioButton x:Name="rbLocation" Content="Location" MinWidth="40" Checked="{Binding GridViewColumnHeader_Click}"/>
I tried to do the same thing with a binding, not not sure if that's the right way. I was getting a error when trying to do a binding:
System.Windows.Markup.XamlParseException: ''Provide value on 'System.Windows.Data.Binding' threw an exception.'
I think this doesn't work because I'm not binding it to anything, because it's an event.
The listview is defined as such:
<ListView Name="lstView" SelectionChanged="lstView_SelectionChanged_1" MouseDoubleClick="LstView_MouseDoubleClick" Grid.Column="0" Background="LightGray" Margin="10,10,0,10" Grid.RowSpan="3">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Width="Auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding PCID}"></TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
<GridViewColumn.Header>
<GridViewColumnHeader>
<TextBlock Text="PCID" HorizontalAlignment="Stretch"></TextBlock>
</GridViewColumnHeader>
</GridViewColumn.Header>
</GridViewColumn>
<GridViewColumn Width="Auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding ProductionArea}"></TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
<GridViewColumn.Header>
<GridViewColumnHeader Tag="ProductionArea" Click="GridViewColumnHeader_Click">
<TextBlock Text="ProductionArea"></TextBlock>
</GridViewColumnHeader>
</GridViewColumn.Header>
</GridViewColumn>
<GridViewColumn Width="Auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Type}"></TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
<GridViewColumn.Header>
<GridViewColumnHeader Tag="Type" Click="GridViewColumnHeader_Click">
<TextBlock Text="Type"></TextBlock>
</GridViewColumnHeader>
</GridViewColumn.Header>
</GridViewColumn>
<GridViewColumn Width="auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Location}"></TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
<GridViewColumn.Header>
<GridViewColumnHeader Tag="Location" Click="GridViewColumnHeader_Click">
<TextBlock Text="Location"></TextBlock>
</GridViewColumnHeader>
</GridViewColumn.Header>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
I figured I could use the tags to point to the header since everything has the same name, but I haven't been able to figure out the right wording. For example, how do I pass the Location header into the GridViewColumnHeader_Click method from outside of the click event? Thanks in advance, very stuck on this one.
To be honest, I don't understand what your problem is. In fact, you already have everything implemented.
Just in case, I'll show you an example.
using System;
using System.Collections.ObjectModel;
using System.Linq;
namespace Core2022.SO.AlexD.ManageGridViewColumn
{
public class SomeItem
{
public string Word { get; }
public int Value { get; }
public SomeItem(string word, int value)
{
Word = word;
Value = value;
}
private static ObservableCollection<SomeItem>? _itemsExample;
public static ObservableCollection<SomeItem> ItemsExample
{
get
{
if (_itemsExample is null)
{
Random random = new Random();
_itemsExample = new ObservableCollection<SomeItem>
(
"How to pass a GridViewColumnHeader object to a method?"
.Split()
.Select(word => new SomeItem(word, random.Next()))
);
}
return _itemsExample;
}
}
}
}
<Window x:Class="Core2022.SO.AlexD.ManageGridViewColumn.ManagerWindow"
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:Core2022.SO.AlexD.ManageGridViewColumn"
xmlns:specialized="clr-namespace:System.Collections.Specialized;assembly=netstandard"
xmlns:sys="clr-namespace:System;assembly=netstandard"
xmlns:componentmodel="clr-namespace:System.ComponentModel;assembly=System.ComponentModel.TypeConverter"
mc:Ignorable="d"
Title="ManagerWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ListView x:Name="itemsView" Grid.Row="1"
ItemsSource="{x:Static local:SomeItem.ItemsExample}">
<ListView.View>
<GridView>
<GridViewColumn Header="Word">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Word}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Value">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<UniformGrid Columns="2" Rows="2">
<TextBlock Text="SortDirection"/>
<TextBlock Text="Column"/>
<ComboBox x:Name="directionsView"
SelectedIndex="0"
SelectionChanged="OnDirectionChanged">
<ComboBox.ItemsSource>
<CompositeCollection>
<componentmodel:ListSortDirection>Ascending</componentmodel:ListSortDirection>
<componentmodel:ListSortDirection>Descending</componentmodel:ListSortDirection>
</CompositeCollection>
</ComboBox.ItemsSource>
</ComboBox>
<ComboBox x:Name="propertiesView"
SelectedIndex="0"
SelectionChanged="OnPropertyChanged">
<ComboBox.ItemsSource>
<specialized:StringCollection>
<sys:String>Un Sort</sys:String>
<sys:String>Word</sys:String>
<sys:String>Value</sys:String>
</specialized:StringCollection>
</ComboBox.ItemsSource>
</ComboBox>
</UniformGrid>
</Grid>
</Window>
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace Core2022.SO.AlexD.ManageGridViewColumn
{
public partial class ManagerWindow : Window
{
public ManagerWindow()
{
InitializeComponent();
}
private string oldProperty = string.Empty;
private ListSortDirection oldDirection;
private void OnPropertyChanged(object sender, SelectionChangedEventArgs e)
{
string property = (string)propertiesView.SelectedItem;
itemsView.Items.SortDescriptions.Clear();
if (property is not nameof(SomeItem.Word) and not nameof(SomeItem.Value))
{
oldProperty = string.Empty;
directionsView.Visibility = Visibility.Hidden;
}
else if (property != oldProperty)
{
oldProperty = property;
directionsView.Visibility = Visibility.Visible;
oldDirection = ListSortDirection.Ascending;
directionsView.SelectedIndex = 0;
itemsView.Items.SortDescriptions.Add(new SortDescription(property, oldDirection));
}
}
private void OnDirectionChanged(object sender, SelectionChangedEventArgs e)
{
ListSortDirection direction = (ListSortDirection)directionsView.SelectedItem;
if (direction != oldDirection)
{
oldDirection = direction;
itemsView.Items.SortDescriptions.Clear();
itemsView.Items.SortDescriptions.Add(new SortDescription(oldProperty, direction));
}
}
}
}

Find Controls placed inside ListView Wpf

<ListView Height="Auto" Name="lstIndent" SelectionMode="Single" Grid.ColumnSpan="5" HorizontalAlignment="Stretch" VerticalAlignment="Top" Grid.Row="0" >
<ListView.View >
<GridView x:Name="dgIndentDetails" >
<GridViewColumn Width="Auto" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<!--<GroupBox Header="Department Issue Header" Grid.Row="5" Grid.ColumnSpan="5" HorizontalAlignment="Stretch" >-->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<CheckBox Grid.Row="0" Grid.Column="0" IsChecked="{Binding Path=IsIndentIssue, UpdateSourceTrigger=PropertyChanged}" x:Name="chkbxIsChecked" HorizontalAlignment="Center"/>
<Label Grid.Column="1" Grid.Row="0" Content="{Binding ItemName}" x:Name="lbllstItemName" HorizontalAlignment="Center" ></Label>
<Label Grid.Column="2" Grid.Row="0" Content="{Binding Quantity}" x:Name="lbllstQty" HorizontalAlignment="Center" ></Label>
<Label Grid.Column="3" Grid.Row="0" Content="{Binding IssueQuantity}" x:Name="lbllstIssuedQty" HorizontalAlignment="Center" ></Label>
<DataGrid Height="Auto" Padding="10,10,10,10" Width="800" Grid.Row="1" Grid.ColumnSpan="5" Name="dgIssuedItemsBatchDetails" AutoGenerateColumns="False" HorizontalAlignment="Stretch" VerticalAlignment="Center">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding ItemName}">
<DataGridTextColumn.Header>
<TextBlock Text="ItemName"/>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding BatchNo}">
<DataGridTextColumn.Header>
<TextBlock Text="BatchNo"/>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding Quantity}">
<DataGridTextColumn.Header>
<TextBlock Text="Quantity"/>
</DataGridTextColumn.Header>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
<!--</GroupBox>-->
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
I need to find DataGrid dgIssuedItemsBatchDetails and then bind. That DataGrid is placed inside GridView and that GridView is placed inside ListView.
GridView grdvwIssueDetail = (GridView)lstIndent.FindName("dgIndentDetails");
By using this code I am able to find the GridView. My DataGrid is placed inside GridView DataTemplate. So suggest me how I can find my DataGrid from code behind.
It can be achieved by using VisualTreeHelper.
As MSDN says:
VisualTreeHelper class provides utility methods that perform common tasks involving nodes in a visual tree.
So let me show an example how it can be achieved:
private void SeeTheChild()
{
DataGrid myCombo = GetVisualChildInDataTemplate<DataGrid>(lstIndent);
}
private T GetVisualChildInDataTemplate<T>(DependencyObject parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChildInDataTemplate<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
VisualTreeHelper.GetChildrenCount(parent) returns 0 cause ListView is created, but not yet loaded. Particularly, the DataTemplate has not yet been applied to the ListView, consequently there is nothing in the Visual Tree.
Then use this method to get your DataGrid on Loaded event of your Window:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
SeeTheChild();
}
Update:
To find a control per item in ListView:
Create a SelectionChanged event:
private void listView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ItemContainerGenerator generator = this.listView.ItemContainerGenerator;
ListBoxItem selectedItem = (ListBoxItem)generator.ContainerFromIndex(listView.SelectedIndex);
Label aLabel = GetChildrenByType(selectedItem, typeof(Label), "label") as Label;
if (aLabel != null)
{
MessageBox.Show("We've found Label with name 'label': " + aLabel.Content);
}
else
{
MessageBox.Show("There is no such Label");
}
}
and a method which can find a necessary control for you:
public static Visual GetChildrenByType(Visual visualElement, Type typeElement, string nameElement)
{
if (visualElement == null) return null;
if (visualElement.GetType() == typeElement)
{
FrameworkElement fe = visualElement as FrameworkElement;
if (fe != null)
{
if (fe.Name == nameElement)
{
return fe;
}
}
}
Visual foundElement = null;
if (visualElement is FrameworkElement)
(visualElement as FrameworkElement).ApplyTemplate();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visualElement); i++)
{
Visual visual = VisualTreeHelper.GetChild(visualElement, i) as Visual;
foundElement = GetChildrenByType(visual, typeElement, nameElement);
if (foundElement != null)
break;
}
return foundElement;
}

How to resize ListView/GridView to contents in WPF

I would like to resize columns of a grid view into ListView to contents. There is the XAML code:
<ListView x:Name="listViewStatEntries"
AlternationCount="2" Grid.Row="8" Grid.ColumnSpan="3" Background="White"
ItemsSource="{Binding StatResult.StatEntries}" >
<ListView.View>
<GridView>
<GridViewColumn Width="Auto" DisplayMemberBinding="{Binding Param.Speciality}">
<GridViewColumn.Header>
<GridViewColumnHeader Content="{Binding Labels[2851]}" Tag="Param.Speciality" Click="listViewStatEntriesColumnHeader_Click" Width="Auto" />
</GridViewColumn.Header>
</GridViewColumn>
<GridViewColumn Width="Auto" DisplayMemberBinding="{Binding Alarm}">
<GridViewColumn.Header>
<GridViewColumnHeader Content="{Binding Labels[2765]}" Tag="Alarm" Click="listViewStatEntriesColumnHeader_Click" Width="Auto" />
</GridViewColumn.Header>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
I found this thread: Force resize of GridView columns inside ListView and try Oskar solution:
public void AutoSizeColumns()
{
GridView gv = listView1.View as GridView;
if (gv != null)
{
foreach (var c in gv.Columns)
{
// Code below was found in GridViewColumnHeader.OnGripperDoubleClicked() event handler (using Reflector)
// i.e. it is the same code that is executed when the gripper is double clicked
if (double.IsNaN(c.Width))
{
c.Width = c.ActualWidth;
}
c.Width = double.NaN;
}
}
}
But with this solution, columns was resized to visible contents only and not to all data into the ListView. How can I resize column to all data?
Thanks in advance for your help !
The reason they resize only to the visible rows is because you have virtualization on (which is default). Try turning it off
<ListView VirtualizingStackPanel.IsVirtualizing="False"

Dropping an item on an UserControl item in GridView doesnt trigger Drop event (includes sample project)

I want to be able to drag an item from a ListView, and drop it onto the a GridView that represents a list of UserControls.
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListView
ItemsSource="{Binding}"
VerticalAlignment="Stretch" HorizontalAlignment="Left"
CanReorderItems="False" CanDragItems="True"
DragItemsStarting="DragItemsStarting">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Width="280">
<TextBlock Text="{Binding}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Viewbox Grid.Column="1" Margin="0,20,0,0">
<GridView x:Name="BoardGrid"
Width="600" Height="600">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid MaximumRowsOrColumns="10"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.ItemTemplate>
<DataTemplate x:Name="GameBoardCellWithPieceDataTemplate">
<Grid Background="Green" Width="50" Height="50">
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</Viewbox>
</Grid>
In the code behind I create the GridItems, set the AllowDrop property an assign an eventhander to the Drop event.
public MainPage()
{
this.InitializeComponent();
Loaded += MainPage_Loaded;
this.DataContext = new List<string> { "Drag String 1", "Drag String 2" };
}
public void MainPage_Loaded(object sender, RoutedEventArgs e)
{
for (var row = 0; row < 5; row++)
{
for (var column = 0; column < 5; column++)
{
var myUserControl = new MyUserControl { AllowDrop = true };
myUserControl.Drop += PieceDropped;
BoardGrid.Items.Add(myUserControl);
}
}
}
private void DragItemsStarting(object sender, DragItemsStartingEventArgs e)
{
var dragItem = (string)e.Items.FirstOrDefault();
if (dragItem == null)
return;
e.Data.Properties.Add("dragItem", dragItem);
}
private void PieceDropped(object sender, DragEventArgs e)
{
object sourceItem;
e.Data.Properties.TryGetValue("dragItem", out sourceItem);
if (sourceItem == null)
return;
[...]
}
The drag behaviour works fine, but when I drop an item of the ListView onto the GridView(Item), the Drop event isnt fired.
When I set the AllowDrop on the GridView itself, the Drop event is fired. Or when I wrap the UserControl in a GridViewItem, and set the AllowDrop on the GridViewItem, also the Drop event is fired, but after that i'm having problems with the DataTemplateSelecdtor I use.
Any clue?
I have created a sample project (http://sdrv.ms/V2OpfE) to demonstrate the problem

Locate position of control that was clicked in GridView (WPF)

I'm using a ListView that contains a GridView, there's a gridviewcolumn which only contains buttons. In the buttonclick event, how do I get the position (I just need the row in this case) of the button which fired the event?
<ListView.View>
<GridView>
<GridViewColumn Width="Auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Width="Auto" Height="Auto" Background="#00000000" BorderBrush="#00000000" BorderThickness="0" Click="Finalizado_Click">
<Button.Content>
<Image Source="Content/okay.png" Width="8" Height="8"/>
</Button.Content>
</Button>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
I know the columns are automatically autogeneratedfields, if you change them to buttonfields you might be able to get a little more information. Sorry I can't help more than that.
public ListViewItem GetRow(DependencyObject item)
{
DependencyObject dObj = VisualTreeHelper.GetParent(item);
if (dObj == null)
return null;
if (dObj is ListViewItem)
return dObj as ListViewItem;
return GetRow(dObj);
}

Categories