我计划创建一个典型的Master-Detail场景,即通过DataBinding向ListView
显示在ICollectionView
中的项目集合,以及单独控件组中所选项目的详细信息( TextBoxes,NumUpDowns ......)。
到目前为止没问题,实际上我已经在旧项目中实现了一个非常类似的场景。但是,应该可以在ListView中选择多个项目,并在详细信息视图中显示相应的共享值。这意味着,如果所有选定项目的属性值相同,则此值应显示在详细信息视图中。如果它们不共享相同的值,则相应的控件应该为用户指示这一点提供一些视觉线索,并且不应显示任何值(或者例如CheckBox
中的“未定义”状态)。现在,如果用户编辑了该值,则此更改应应用于所有所选项目。
进一步的要求是:
有没有人有这种情况的经验?实际上,我认为这应该是一个非常常见的情况。但是,我无法在任何地方找到有关该主题的任何细节。
谢谢!
gehho。
PS:在上面提到的旧项目中,我有一个使用ViewModel子类的解决方案,它处理多选的特殊情况。它检查了所有选定项目是否相等并返回了适当的值。然而,这种方法有一些缺点,并且在某种程度上看起来像是一个黑客,因为(除了其他臭的东西),有必要打破ListView
和详细视图之间的同步并手动处理它。
答案 0 :(得分:1)
我遇到了同样的问题。
IsSynchronizedWithCurrentItem = "True"
仅保持CurrentItem同步(您选择的最后一项不保持ctrl或shift)。
就像你可能有的一样,我已经在互联网上搜索了一个答案,从来没有想过一个。我的场景有一个3层Master> Detail> Detail绑定,其中每个层绑定到它自己的ListBox。
我已经装好了暂时有用的东西。
For my Master> Detail> Detail tiers我为每个层创建了一个单独的CollectionViewSource,并将CollectionViewSource.Source设置为适当的实体对象。在MasterView绑定ListBox的SelectionChanged事件上,我在MasterView.View上执行了一个过滤器,以检索主主键=详细外键的对象。
这很草率,但如果你找到了更好的方法来完成这件事,我很乐意听到它。
答案 1 :(得分:1)
在ViewModel中,创建一个将绑定到ListView的SelectedItems的属性。
创建另一个属性,代表所选项目的详细信息对象。
详细信息部分(在XAML中)绑定到此详细信息属性(在ViewModel中)。
每次修改选定的项目集合(setter / CollectionChanged事件)时,您还需要更新您的详细信息对象(这将迭代相关属性并确定它们是否具有相同的值)。
修改详细信息对象中的属性后,您需要重新选择所选项目集合并对其进行相关更改。
就是这样。
希望有所帮助
答案 2 :(得分:0)
我使用了与the one suggested by Captain类似的方法。我在我的ViewModel中创建了一个属性,它表示多个项目(即所有选定的项目)。在属性get-和set-accessors中,我使用以下方法来确定/设置/的所有项的共享值。这种方法不使用任何反射,但使用lambda表达式形式的委托,这使得它非常快。
概述,这是我的基本设计:
public class MyMultiSelectionViewModel
{
private List<MyItemType> m_selectedItems = new List<MyItemType>();
public void UpdateSelectedItems(IList<MyItemType> selectedItems)
{
m_selectedItems = selectedItems;
this.OnPropertyChanged(() => this.MyProperty1);
this.OnPropertyChanged(() => this.MyProperty2);
// and so on for all relevant properties
}
// properties using SharedValueHelper (see example below)
}
属性如下所示:
public string Name
{
get
{
return SharedValueHelper.GetSharedValue<MyItemType, string>(m_selectedItems, (item) => item.Name, String.Empty);
}
set
{
SharedValueHelper.SetSharedValue<MyItemType, string>(m_selectedItems, (item, newValue) => item.Name = newValue, value);
this.OnPropertyChanged(() => this.Name);
}
}
SharedValueHelper
类的代码如下所示:
/// <summary>
/// This static class provides some methods which can be used to
/// retrieve a <i>shared value</i> for a list of items. Shared value
/// means a value which represents a common property value for all
/// items. If all items have the same property value, this value is
/// the shared value. If they do not, a specified <i>non-shared value</i>
/// is used.
/// </summary>
public static class SharedValueHelper
{
#region Methods
#region GetSharedValue<TItem, TProperty>(IList<TItem> items, Func<TItem, TProperty> getPropertyDelegate, TProperty nonSharedValue)
/// <summary>
/// Gets a value for a certain property which represents a
/// <i>shared</i> value for all <typeparamref name="TItem"/>
/// instances in <paramref name="items"/>.<br/>
/// This means, if all wrapped <typeparamref name="TItem"/> instances
/// have the same value for the specific property, this value will
/// be returned. If the values differ, <paramref name="nonSharedValue"/>
/// will be returned.
/// </summary>
/// <typeparam name="TItem">The type of the items for which a shared
/// property value is requested.</typeparam>
/// <typeparam name="TProperty">The type of the property for which
/// a shared value is requested.</typeparam>
/// <param name="items">The collection of <typeparamref name="TItem"/>
/// instances for which a shared value is requested.</param>
/// <param name="getPropertyDelegate">A delegate which returns the
/// property value for the requested property. This is used, so that
/// reflection can be avoided for performance reasons. The easiest way
/// is to provide a lambda expression like this:<br/>
/// <code>(item) => item.MyProperty</code><br/>
/// This expression will simply return the value of the
/// <c>MyProperty</c> property of the passed item.</param>
/// <param name="nonSharedValue">The value which should be returned if
/// the values are not equal for all items.</param>
/// <returns>If all <typeparamref name="TItem"/> instances have
/// the same value for the specific property, this value will
/// be returned. If the values differ, <paramref name="nonSharedValue"/>
/// will be returned.</returns>
public static TProperty GetSharedValue<TItem, TProperty>(IList<TItem> items, Func<TItem, TProperty> getPropertyDelegate, TProperty nonSharedValue)
{
if (items == null || items.Count == 0)
return nonSharedValue;
TProperty sharedValue = getPropertyDelegate(items[0]);
for (int i = 1; i < items.Count; i++)
{
TItem currentItem = items[i];
TProperty currentValue = getPropertyDelegate(currentItem);
if (!sharedValue.Equals(currentValue))
return nonSharedValue;
}
return sharedValue;
}
#endregion
#region SetSharedValue<TItem, TProperty>(IList<TItem> a_items, Action<TItem, TProperty> a_setPropertyDelegate, TProperty a_newValue)
/// <summary>
/// Sets the same value for all <typeparamref name="TItem"/>
/// instances in <paramref name="a_items"/>.
/// </summary>
/// <typeparam name="TItem">The type of the items for which a shared
/// property value is requested.</typeparam>
/// <typeparam name="TProperty">The type of the property for which
/// a shared value is requested.</typeparam>
/// <param name="items">The collection of <typeparamref name="TItem"/>
/// instances for which a shared value should be set.</param>
/// <param name="setPropertyDelegate">A delegate which sets the
/// property value for the requested property. This is used, so that
/// reflection can be avoided for performance reasons. The easiest way
/// is to provide a lambda expression like this:<br/>
/// <code>(item, newValue) => item.MyProperty = newValue</code><br/>
/// This expression will simply set the value of the
/// <c>MyProperty</c> property of the passed item to <c>newValue</c>.</param>
/// <param name="newValue">The new value for the property.</param>
public static void SetSharedValue<TItem, TProperty>(IList<TItem> items, Action<TItem, TProperty> setPropertyDelegate, TProperty newValue)
{
if (items == null || items.Count == 0)
return;
foreach (TItem item in items)
{
try
{
setPropertyDelegate(item, newValue);
}
catch (Exception ex)
{
// log/error message here
}
}
}
#endregion
#endregion
}