我想创建一个ListView,它有两列具有固定宽度和第三列以填充剩余空间。所以像这样:
<ListView>
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="*" />
<GridViewColumn Header="Age" Width="50" />
<GridViewColumn Header="Gender" Width="50" />
</GridView>
</ListView.View>
</ListView>
问题是我找不到让Name
列填充剩余空间的方法,因为将宽度设置为*
不起作用。看起来有一种方法可以使用value converter进行此操作,但似乎应该有一种更简单的方法。与DataGrid控件一样,您可以使用*
s指定列的宽度。
答案 0 :(得分:36)
我试图实现相同的目标但后来决定我希望我的ListView列使用ListView的一定百分比,结果是所有列占用了一部分空间并且所有空间都在ListView中使用。您可以将其设置为在最后一列中包含您喜欢的任何百分比,以直接在最后一列上填充剩余空间&#39;目标
我发现这种方法相当健壮和可靠(即使调整大小!),所以我想也许可以分享。
我的ListView中有四列用于此示例。您只需使用以下事件处理程序在ListView中注册SizeChanged
事件:
private void ProductsListView_SizeChanged(object sender, SizeChangedEventArgs e)
{
ListView listView = sender as ListView;
GridView gView = listView.View as GridView;
var workingWidth = listView.ActualWidth - SystemParameters.VerticalScrollBarWidth; // take into account vertical scrollbar
var col1 = 0.50;
var col2 = 0.20;
var col3 = 0.15;
var col4 = 0.15;
gView.Columns[0].Width = workingWidth*col1;
gView.Columns[1].Width = workingWidth*col2;
gView.Columns[2].Width = workingWidth*col3;
gView.Columns[3].Width = workingWidth*col4;
}
答案 1 :(得分:14)
当遇到类似的问题时遇到了这个问题,我的问题是我希望所有列都是'Auto',期望第一个,这只会填补额外的空间,所以我扩展了GONeale的解决方案。
private void ListView_SizeChanged(object sender, SizeChangedEventArgs e)
{
ListView _ListView = sender as ListView;
GridView _GridView = _ListView.View as GridView;
var _ActualWidth = _ListView.ActualWidth - SystemParameters.VerticalScrollBarWidth;
for (Int32 i = 1; i < _GridView.Columns.Count; i++)
{
_ActualWidth = _ActualWidth - _GridView.Columns[i].ActualWidth;
}
_GridView.Columns[0].Width = _ActualWidth;
}
然后XAML就是:
...
<ListView.View>
<GridView>
<GridViewColumn Header="Title" />
<GridViewColumn Header="Artist" Width="Auto" />
<GridViewColumn Header="Album" Width="Auto" />
<GridViewColumn Header="Genre" Width="Auto" />
</GridView>
</ListView.View>
...
此代码也可以更通用地使用,因为列数不是硬编码的,稍微调整一下,你可以通过某种逻辑来定义'填充列'。
希望它可以帮助某人:)
答案 2 :(得分:7)
问题是GridViewColumn的列宽是double,而不是GridLength对象,并且没有适当的转换来处理*。不确定这是否是WPF团队的疏忽。你会认为应该支持它。
除了转换器之外,我见过它的另一种方式就是:http://www.ontheblog.net/CMS/Default.aspx?tabid=36&EntryID=37。
两者都是不需要的额外工作。我用ListView和GridView组合找到了其他“怪异”的东西,所以我放弃使用它们。如果我需要数据网格,我使用我们许可的第三方,如果我需要一个复杂的ListBox样式菜单,我只使用一个模板化的ListBox。
答案 3 :(得分:1)
我需要让所有列具有相同的宽度。上面的解决方案很好,但我更喜欢将这样的东西包装在附加属性(MVVM,可重用性等)中。这是我的代码,如果它可以帮助。
public class StarSizeHelper {
private static readonly List<FrameworkElement> s_knownElements = new List<FrameworkElement>();
public static bool GetIsEnabled(DependencyObject d) {
return (bool) d.GetValue(IsEnabledProperty);
}
public static void SetIsEnabled(ListView d, bool value) {
d.SetValue(IsEnabledProperty, value);
}
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached("IsEnabled",
typeof(bool),
typeof(StarSizeHelper),
new FrameworkPropertyMetadata(IsEnabledChanged));
public static void IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
var ctl = d as ListView;
if (ctl == null) {
throw new Exception("IsEnabled attached property only works on a ListView type");
}
RememberElement(ctl);
}
private static void RememberElement(ListView ctl) {
if (! s_knownElements.Contains(ctl)) {
s_knownElements.Add(ctl);
RegisterEvents(ctl);
}
// nothing to do if elt is known
}
private static void OnUnloaded(object sender, RoutedEventArgs e) {
FrameworkElement ctl = (FrameworkElement) sender;
ForgetControl(ctl);
}
private static void ForgetControl(FrameworkElement fe) {
s_knownElements.Remove(fe);
UnregisterEvents(fe);
}
private static void RegisterEvents(FrameworkElement fe) {
fe.Unloaded += OnUnloaded;
fe.SizeChanged += OnSizeChanged;
}
private static void UnregisterEvents(FrameworkElement fe) {
fe.Unloaded -= OnUnloaded;
fe.SizeChanged -= OnSizeChanged;
}
private static void OnSizeChanged(object sender, SizeChangedEventArgs e) {
ListView listView = sender as ListView;
if (listView == null) {
return; // should not happen
}
GridView gView = listView.View as GridView;
if (gView == null) {
return; // should not happen
}
var workingWidth = listView.ActualWidth - SystemParameters.VerticalScrollBarWidth -10; // take into account vertical scrollbar
var colWidth = workingWidth / gView.Columns.Count;
foreach (GridViewColumn column in gView.Columns) {
column.Width = colWidth;
}
}
}
为了使用它:
<ListView ... StarSizeHelper.IsEnabled="true" ... />
(当然,你还要在XAML中修复名称空间声明)
您可以在OnSizeChanged方法中调整您的大小调整需求。
答案 4 :(得分:1)
即使博客仍然存在,大卫汉森 - 格雷维尔的OnTheBlog在第一个答案之一中提到的解决方案已不再可用。我能够在Wayback Machine上找到它并进行一些调整,这里是:
诀窍是你在ListView上设置Stretch = true,它将拉伸没有相同宽度的列。
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
namespace Demo.Extension.Properties
{
///
/// ListViewColumnStretch
///
public class ListViewColumns : DependencyObject
{
///
/// IsStretched Dependancy property which can be attached to gridview columns.
///
public static readonly DependencyProperty StretchProperty =
DependencyProperty.RegisterAttached("Stretch",
typeof(bool),
typeof(ListViewColumns),
new UIPropertyMetadata(true, null, OnCoerceStretch));
///
/// Gets the stretch.
///
/// The obj.
///
public static bool GetStretch(DependencyObject obj)
{
return (bool)obj.GetValue(StretchProperty);
}
///
/// Sets the stretch.
///
/// The obj.
/// if set to true [value].
public static void SetStretch(DependencyObject obj, bool value)
{
obj.SetValue(StretchProperty, value);
}
///
/// Called when [coerce stretch].
///
///If this callback seems unfamilar then please read
/// the great blog post by Paul Jackson found here.
/// http://compilewith.net/2007/08/wpf-dependency-properties.html
/// The source.
/// The value.
///
public static object OnCoerceStretch(DependencyObject source, object value)
{
ListView lv = (source as ListView);
//Ensure we dont have an invalid dependancy object of type ListView.
if (lv == null)
{
throw new ArgumentException("This property may only be used on ListViews");
}
//Setup our event handlers for this list view.
lv.Loaded += new RoutedEventHandler(lv_Loaded);
lv.SizeChanged += new SizeChangedEventHandler(lv_SizeChanged);
return value;
}
///
/// Handles the SizeChanged event of the lv control.
///
/// The source of the event.
/// The instance containing the event data.
private static void lv_SizeChanged(object sender, SizeChangedEventArgs e)
{
ListView lv = (sender as ListView);
if (lv.IsLoaded)
{
//Set our initial widths.
SetColumnWidths(lv);
}
}
///
/// Handles the Loaded event of the lv control.
///
/// The source of the event.
/// The instance containing the event data.
private static void lv_Loaded(object sender, RoutedEventArgs e)
{
ListView lv = (sender as ListView);
//Set our initial widths.
SetColumnWidths(lv);
}
///
/// Sets the column widths.
///
private static void SetColumnWidths(ListView listView)
{
//Pull the stretch columns fromt the tag property.
List<GridViewColumn> columns = (listView.Tag as List<GridViewColumn>);
double specifiedWidth = 0;
GridView gridView = listView.View as GridView;
if (gridView != null)
{
if (columns == null)
{
//Instance if its our first run.
columns = new List<GridViewColumn>();
// Get all columns with no width having been set.
foreach (GridViewColumn column in gridView.Columns)
{
if (!(column.Width >= 0))
{
columns.Add(column);
}
else
{
specifiedWidth += column.ActualWidth;
}
}
}
else
{
// Get all columns with no width having been set.
foreach (GridViewColumn column in gridView.Columns)
{
if (!columns.Contains(column))
{
specifiedWidth += column.ActualWidth;
}
}
}
// Allocate remaining space equally.
foreach (GridViewColumn column in columns)
{
double newWidth = (listView.ActualWidth - specifiedWidth) / columns.Count;
if (newWidth >= 10)
{
column.Width = newWidth - 10;
}
}
//Store the columns in the TAG property for later use.
listView.Tag = columns;
}
}
}
}
您只需将命名空间添加到XAML文件
xmlns:Extensions="clr-namespace:Demo.Extension.Properties"
并在列表视图中使用它:
<ListView ItemsSource="{Binding Path=Items}" DisplayMemberPath="Name"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Grid.Column="0" Margin="8" Extensions:ListViewColumns.Stretch="true">
答案 5 :(得分:0)
我的问题类似,但我想修复第一列的宽度,如果我添加或删除列,我也不希望它破坏,即使在运行时也是如此。感谢@Gary提供https://stackoverflow.com/a/14674830/492
的提示private void ResultsListView_SizeChanged(object sender, SizeChangedEventArgs e)
{
double newWidthForColumnsExceptFirstColumn = ResultsListView.ActualWidth - SystemParameters.VerticalScrollBarWidth - ResultsGridView.Columns[0].Width;
int columnsCount = ResultsGridView.Columns.Count;
Double newColumnWidth = newWidthForColumnsExceptFirstColumn / (columnsCount -1);
for ( int col = 1; col < columnsCount; col++ ) // skip column [0]
{
ResultsGridView.Columns[col].Width = newColumnWidth;
}
}
答案 6 :(得分:0)
这是一个允许多个ListView利用一般“Resize”事件处理程序的解决方案。
//Using dictionarys as trackers allows us to have multiple ListViews use the same code
private Dictionary<string, double> _fixedWidthTracker = new Dictionary<string, double>();
private Dictionary<string, List<GridViewColumn>> _varWidthColTracker = new Dictionary<string, List<GridViewColumn>>();
private void ListView_SizeChanged(object sender, SizeChangedEventArgs e)
{
ListView lv = sender as ListView;
if (lv != null)
{
//For validation during Debug
VerifyName(lv);
GridView gv = lv.View as GridView;
if (gv != null)
{
if (!_varWidthColTracker.ContainsKey(lv.Name))
{
_varWidthColTracker[lv.Name] = new List<GridViewColumn>();
_fixedWidthTracker[lv.Name] = 0;
foreach (GridViewColumn gvc in gv.Columns)
{
if (!double.IsNaN(gvc.Width)) _fixedWidthTracker[lv.Name] += gvc.Width; else _varWidthColTracker[lv.Name].Add(gvc);
}
}
double newWidthForColumns = e.NewSize.Width - SystemParameters.VerticalScrollBarWidth - _fixedWidthTracker[lv.Name];
int columnsCount = gv.Columns.Count;
int numberOfFixedWithColumns = columnsCount - _varWidthColTracker[lv.Name].Count;
Double newColumnWidth = newWidthForColumns / (columnsCount - numberOfFixedWithColumns);
foreach (GridViewColumn gvc in _varWidthColTracker[lv.Name])
{
gvc.Width = newColumnWidth;
}
}
}
}
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyName(ListView listView)
{
if (String.IsNullOrEmpty(listView.Name))
{
string msg = "The Name attribute is required to be set on the ListView in order to Bind to this method";
Debug.Fail(msg);
}
}
答案 7 :(得分:0)
我采用了上面的例子(这很棒)并稍微改进了一下以防止调整大小时的运行时异常:
private void tpList_SizeChanged(object sender, SizeChangedEventArgs e)
{
ListView listView = sender as ListView;
GridView gView = listView.View as GridView;
var workingWidth = listView.ActualWidth - (SystemParameters.VerticalScrollBarWidth + 20); // take into account vertical scrollbar
var col1 = 0.50;
var col2 = 0.50;
var t1 = workingWidth * col1;
var t2 = workingWidth * col2;
gView.Columns[0].Width = t1 > 0 ? t1 : 1;
gView.Columns[1].Width = t2 > 0 ? t2 : 1;
}
}