这是使用WPF和Datagrid的问题。
有许多对象,用户希望在表中看到行,其中列是日期。
| Name |Jan 2014|Feb 2014|Mar 2014|...|
--------------------------------------------
| coolObj1 | 10.0| 10.0| 20.0|...|
| coolObj2 | 15.0| 19.0| 25.0|...|
我的问题:
在Qt中有很多方法可以处理这样的情况(使用QAbstractItemModel Luke!)
XAML代码:
<Window x:Class="Columns.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Find easter eggs" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Row="0" HorizontalAlignment="Left" Margin="5" Click="OnClick">Update grid</Button>
<DataGrid x:Name="Grid"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserResizeRows="False"
CanUserReorderColumns="False"
CanUserResizeColumns="True"
CanUserSortColumns="False"
Grid.Row="1" />
</Grid>
</Window>
Xaml.cs代码:
namespace Columns
{
public partial class MainWindow
{
private SomeDict _dict;
public MainWindow()
{
InitializeComponent();
GetData();
UpdateView();
}
private void GetData()
{
// We assume that there can be a lot of dicts
// Currently we work with the only one
_dict = new SomeDict();
Random rnd = new Random();
var start = new DateTime(2010, 1, 1);
var end = new DateTime(2020, 1, 1);
for (DateTime date = start; date < end; date = date.AddMonths(1))
{
_dict.Add(date, rnd.NextDouble());
}
}
private void UpdateView()
{
var row = new RowContext();
// Create name column
string columnName = "Name";
ICellContext cellContext = new NameCell("Easter egg");
row[columnName] = cellContext;
Grid.Columns.Clear();
Grid.Columns.Add(new DataGridTextColumn()
{
Binding = new Binding(columnName),
Header = columnName,
Width = 100,
});
var stringToDoubleConverter = new StringToDoubleConverter();
// Create column for each date in dictionary
foreach (var pair in _dict)
{
DateTime date = pair.Key;
columnName = AsColumnName(date);
Grid.Columns.Add(new DataGridTextColumn()
{
Binding = new Binding(columnName)
{
StringFormat = "N3",
Mode = BindingMode.TwoWay,
Converter = stringToDoubleConverter,
},
IsReadOnly = false,
Width = 80,
Header = AsReadable(date),
});
cellContext = new CellContext(_dict, date, typeof (double), pair.Value);
row[columnName] = cellContext;
}
// Finally set ItemsSource
Grid.ItemsSource = new[] {row};
}
private string AsReadable(DateTime date)
{
return date.ToString("d");
}
private string AsColumnName(DateTime date)
{
return date.ToString("yy-MM-dd");
}
private void OnClick(object sender, RoutedEventArgs e)
{
UpdateView();
}
}
public interface ICellContext
{
object Value { get; set; }
bool IsEditable { get; }
Type PropertyType { get; }
}
internal sealed class NameCell : ICellContext
{
private string _name;
public NameCell(string name)
{
_name = name;
}
public object Value
{
get { return _name; }
set { }
}
public bool IsEditable
{
get { return false; }
}
public Type PropertyType
{
get { return typeof(string); }
}
}
internal sealed class CellContext : ICellContext, INotifyPropertyChanged
{
private readonly bool _isEditable;
private readonly SomeDict _dict;
private readonly DateTime _date;
private readonly Type _propertyType;
private object _initialValue;
/// <summary>
/// Initializes a new instance of the <see cref="T:System.Object" /> class.
/// </summary>
public CellContext(SomeDict dict, DateTime date, Type propertyType, object initialValue, bool isEditable=true)
{
_dict = dict;
_date = date;
_propertyType = propertyType;
_initialValue = initialValue;
_isEditable = isEditable;
}
public event PropertyChangedEventHandler PropertyChanged;
public object Value
{
get { return _initialValue; }
set
{
Debug.Assert(value.GetType() == PropertyType);
if (Equals(value, _initialValue)) return;
// Create do/undo action, write value
_dict[_date] = Convert.ToDouble(value);
_initialValue = value;
OnPropertyChanged("Value");
}
}
public bool IsEditable
{
get { return _isEditable; }
}
public Type PropertyType
{
get { return _propertyType; }
}
[NotifyPropertyChangedInvocator]
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public sealed class RowContext : DynamicObject, INotifyPropertyChanged
{
private readonly IDictionary<string, object> data;
public RowContext()
{
data = new Dictionary<string, object>();
}
public object this[string columnName]
{
get
{
object value;
if (data.TryGetValue(columnName, out value))
{
var iproperty = value as ICellContext;
if (iproperty != null)
{
return iproperty.Value;
}
return value;
}
return null;
}
set
{
object value1;
if (!data.TryGetValue(columnName, out value1))
{
data.Add(columnName, value);
OnPropertyChanged(columnName);
}
else
{
var iproperty = value1 as ICellContext;
if (iproperty != null)
{
iproperty.Value = value;
OnPropertyChanged(columnName);
}
else if (value1 != value)
{
data[columnName] = value;
OnPropertyChanged(columnName);
}
}
}
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return data.Keys;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = this[binder.Name];
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
this[binder.Name] = value;
return true;
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
internal sealed class SomeDict : Dictionary<DateTime, object>
{
}
}
答案 0 :(得分:1)
似乎只有两种方法可以实现这种行为: