- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
不久前,我为 XamDataGrid 和作为 ObservableCollection 的“业务对象”之间的两种方式同步编写了一个“附加行为”。 XamDataGrid 是源,ObservableCollection as DataSource 是目标。由于特定原因,我没有使用 ListCollectionView。
问题
当 DataGrid 的 DataContext 更改为另一个 Vehicle 时,当前加载的 DataGrid 不会更新行为的 DependencyProperty。
我不知道为什么。
我能想到的唯一解决方案是 Hook DataGrid 的 DataContextChanged 并使用 Path 进行新的 BindingOperation ,然后将其设置为相对于 DataContext 以确定 SelectedItems 属性。但在这种情况下,行为的 DependencyProperty 应设置为 SelectedItems 属性的路径,而不是绑定(bind)。
有以下类(class)
示例模型
public class Vehicle
{
public PassengerList Passengers { get; set; }
}
public class PassengerList : ObservableCollection<Passenger>
{
public PassengerList()
{
SelectedPassengers = new ObservableCollection<Passenger>();
}
public ObservableCollection<Passenger> SelectedPassengers { get; private set; }
}
public class Passenger
{
public string Name { get; set; }
}
<igDG:XamDataGrid DataSource="{Binding Passengers}">
<i:Interaction.Behaviors>
<b:XamDataGridSelectedItemsBehavior SelectedItems="{Binding Path=Passengers.SelectedPssengers}" />
</i:Interaction.Behaviors>
</igDG:XamDataGrid>
public class XamDataGridSelectedItemsBehavior : Behavior<XamDataGrid>, IWeakEventListener
{
#region Properties
private XamDataGrid Grid
{
get { return AssociatedObject as XamDataGrid; }
}
public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register(
"SelectedItems",
typeof(INotifyCollectionChanged),
typeof(XamDataGridSelectedItemsBehavior),
new UIPropertyMetadata(new PropertyChangedCallback(OnSelectedItemsChanged)));
private static void OnSelectedItemsChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if (obj != null)
{
(obj as XamDataGridSelectedItemsBehavior).SelectedItems = (e.NewValue as INotifyCollectionChanged);
}
}
public INotifyCollectionChanged SelectedItems
{
get { return (INotifyCollectionChanged)GetValue(SelectedItemsProperty); }
set
{
// remove old listener
if (SelectedItems != null)
CollectionChangedEventManager.RemoveListener(SelectedItems, this);
SetValue(SelectedItemsProperty, value);
// add new listener
if (SelectedItems != null)
CollectionChangedEventManager.AddListener(SelectedItems, this);
}
}
#endregion
#region Init
/// <summary>
/// Hook up event listeners to the associated object.
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
SelectedItemsChangedEventManager.AddListener(Grid, this);
XamDataGridRecordActivatedEventManager.AddListener(Grid, this);
XamDataGridLoadedEventManager.AddListener(Grid, this);
}
void Grid_RecordActivated(object sender, RecordActivatedEventArgs e)
{
if (_transferingToTarget)
return;
// if the CellClickAction is EnterEditModeIfAllowed, the grid does not always select the actual record
// In our case we want it to always select the record
if (e.Record.DataPresenter.FieldSettings.CellClickAction == CellClickAction.EnterEditModeIfAllowed)
{
TransferSourceToTarget();
}
}
void Grid_Loaded(object sender, RoutedEventArgs e)
{
TransferTargetToSource(true);
}
#endregion
#region Target to Source
/// <summary>
/// When selected items in the target as model has changed, then transfer selected item to grid as the source.
/// Not when transfering from grid to selected items.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void SelectedItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (_transferingToTarget)
return;
TransferTargetToSource(false);
}
private bool _transferingToSource = false;
/// <summary>
/// Transfer selected item in the target as model to the grid as source.
/// </summary>
private void TransferTargetToSource(bool notifyTargetListeners)
{
if (SelectedItems == null)
return;
List<Record> newSelection = new List<Record>();
foreach (var item in (SelectedItems as IList))
{
var record = Grid.Records.FirstOrDefault(r => (r is DataRecord) && ((r as DataRecord).DataItem == item));
if (record != null)
{
newSelection.Add(record);
}
}
_transferingToSource = true;
try
{
Grid.SelectedItems.Records.Clear();
Grid.SelectedItems.Records.AddRange(newSelection.ToArray());
if ((newSelection.Count > 0) && !newSelection.Contains(Grid.ActiveRecord))
{
Grid.ActiveRecord = newSelection.FirstOrDefault();
Grid.ActiveRecord.IsSelected = true;
}
if (notifyTargetListeners)
{
// Hack to notify the target listeners
(SelectedItems as IList).Clear();
foreach (var record in newSelection)
{
(SelectedItems as IList).Add((record as DataRecord).DataItem);
}
}
}
finally
{
_transferingToSource = false;
}
}
#endregion
#region Source to Target
/// <summary>
/// When selected items in the source as grid has changed, then transfer selected item to model as the target.
/// Not when transfering from selected items to grid.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void Grid_SelectedItemsChanged(object sender, SelectedItemsChangedEventArgs e)
{
if (_transferingToSource)
return;
TransferSourceToTarget();
}
private bool _transferingToTarget = false;
/// <summary>
/// Transfer the selected item in the grid as source to the selected item in the target as model.
/// </summary>
private void TransferSourceToTarget()
{
var target = this.SelectedItems as IList;
if (target == null)
return;
_transferingToTarget = true;
try
{
// clear the target first
target.Clear();
// When no item is selected there might still be an active record
if (Grid.SelectedItems.Count() == 0)
{
if (Grid.ActiveDataItem != null)
target.Add(Grid.ActiveDataItem);
else if (Grid.ActiveRecord != null && Grid.ActiveRecord.IsDataRecord)
target.Add((Grid.ActiveRecord as DataRecord).DataItem);
else if (Grid.ActiveCell != null && Grid.ActiveCell.Record != null && Grid.ActiveCell.Record.IsDataRecord)
target.Add((Grid.ActiveCell.Record as DataRecord).DataItem);
}
else
{
// foreach record in the source add it to the target
foreach (var r in Grid.SelectedItems.Records)
{
if (r.IsDataRecord)
{
target.Add((r as DataRecord).DataItem);
}
}
}
}
finally
{
_transferingToTarget = false;
}
}
#endregion
/// <summary>
/// Receive an event and delegate it to the correct eventhandler.
/// </summary>
/// <param name="managerType"></param>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <returns></returns>
bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
if (managerType == typeof(CollectionChangedEventManager))
{
SelectedItems_CollectionChanged(sender, e as NotifyCollectionChangedEventArgs);
return true;
}
else if (managerType == typeof(SelectedItemsChangedEventManager))
{
Grid_SelectedItemsChanged(sender, e as SelectedItemsChangedEventArgs);
return true;
}
else if (managerType == typeof(XamDataGridRecordActivatedEventManager))
{
Grid_RecordActivated(sender, e as RecordActivatedEventArgs);
return true;
}
else if (managerType == typeof(XamDataGridLoadedEventManager))
{
Grid_Loaded(sender, e as RoutedEventArgs);
return true;
}
return false;
}
}
#region EventManagers
public class CollectionChangedEventManager : WeakEventManagerBase<CollectionChangedEventManager, INotifyCollectionChanged>
{
protected override void StartListeningTo(INotifyCollectionChanged source)
{
source.CollectionChanged += DeliverEvent;
}
protected override void StopListeningTo(INotifyCollectionChanged source)
{
source.CollectionChanged -= DeliverEvent;
}
}
public class XamDataGridRecordActivatedEventManager : WeakEventManagerBase<XamDataGridRecordActivatedEventManager, XamDataGrid>
{
protected override void StartListeningTo(XamDataGrid source)
{
source.RecordActivated += DeliverEvent;
}
protected override void StopListeningTo(XamDataGrid source)
{
source.RecordActivated -= DeliverEvent;
}
}
public class XamDataGridLoadedEventManager : WeakEventManagerBase<XamDataGridLoadedEventManager, XamDataGrid>
{
protected override void StartListeningTo(XamDataGrid source)
{
source.Loaded += DeliverEvent;
}
protected override void StopListeningTo(XamDataGrid source)
{
source.Loaded -= DeliverEvent;
}
}
public class SelectedItemsChangedEventManager : WeakEventManagerBase<SelectedItemsChangedEventManager, XamDataGrid>
{
protected override void StartListeningTo(XamDataGrid source)
{
source.SelectedItemsChanged += DeliverEvent;
}
protected override void StopListeningTo(XamDataGrid source)
{
source.SelectedItemsChanged -= DeliverEvent;
}
}
#endregion
#region EventManager base class
// TODO: 10-10-2011 (rdj): Deze class misschien opnemen in het frontend framework? In ieder geval zolang we nog geen .NET 4.5 gebruiken
// http://10rem.net/blog/2012/02/01/event-handler-memory-leaks-unwiring-events-and-the-weakeventmanager-in-wpf-45
/// <summary>
/// Weak event manager base class to provide easy implementation of weak event managers.
/// </summary>
/// <typeparam name="TManager">Type of the manager.</typeparam>
/// <typeparam name="TEventSource">Type of the event source.</typeparam>
public abstract class WeakEventManagerBase<TManager, TEventSource> : WeakEventManager
where TManager : WeakEventManagerBase<TManager, TEventSource>, new()
where TEventSource : class
{
/// <summary>
/// Adds a listener
/// </summary>
/// <param name="source">The source of the event, should be null if listening to static events</param>
/// <param name="listener">The listener of the event. This is the class that will recieve the ReceiveWeakEvent method call</param>
public static void AddListener(object source, IWeakEventListener listener)
{
CurrentManager.ProtectedAddListener(source, listener);
}
/// <summary>
/// Removes a listener
/// </summary>
/// <param name="source">The source of the event, should be null if listening to static events</param>
/// <param name="listener">The listener of the event. This is the class that will recieve the ReceiveWeakEvent method call</param>
public static void RemoveListener(object source, IWeakEventListener listener)
{
CurrentManager.ProtectedRemoveListener(source, listener);
}
/// <inheritdoc/>
protected sealed override void StartListening(object source)
{
StartListeningTo((TEventSource)source);
}
/// <inheritdoc/>
protected sealed override void StopListening(object source)
{
StopListeningTo((TEventSource)source);
}
/// <summary>
/// Attaches the event handler.
/// </summary>
protected abstract void StartListeningTo(TEventSource source);
/// <summary>
/// Detaches the event handler.
/// </summary>
protected abstract void StopListeningTo(TEventSource source);
/// <summary>
/// Gets the current manager
/// </summary>
protected static TManager CurrentManager
{
get
{
var mType = typeof(TManager);
var mgr = (TManager)GetCurrentManager(mType);
if (mgr == null)
{
mgr = new TManager();
SetCurrentManager(mType, mgr);
}
return mgr;
}
}
}
#endregion
最佳答案
我通过添加两个新的依赖属性来让它工作。
数据上下文属性
public static readonly DependencyProperty DataContextProperty = DependencyProperty.Register(
"DataContext",
typeof(object),
typeof(XamDataGridSelectedItemsBehavior),
new PropertyMetadata(DataContextChanged));
private static void DataContextChanged(object obj, DependencyPropertyChangedEventArgs e)
{
var behavior = obj as XamDataGridSelectedItemsBehavior;
var binding = new Binding(behavior.Path) { Source = e.NewValue };
BindingOperations.SetBinding(behavior, XamDataGridSelectedItemsBehavior.SelectedItemsProperty, binding);
}
public static readonly DependencyProperty PathProperty = DependencyProperty.Register(
"Path",
typeof(string),
typeof(XamDataGridSelectedItemsBehavior),
new UIPropertyMetadata(string.Empty, new PropertyChangedCallback(OnPathChanged)));
private static void OnPathChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var behavior = obj as XamDataGridSelectedItemsBehavior;
behavior.Path = e.NewValue as string;
}
public string Path { get; set; }
protected override void OnAttached()
{
base.OnAttached();
SelectedItemsChangedEventManager.AddListener(Grid, this);
XamDataGridRecordActivatedEventManager.AddListener(Grid, this);
XamDataGridLoadedEventManager.AddListener(Grid, this);
BindingOperations.SetBinding(this, XamDataGridSelectedItemsBehavior.DataContextProperty, new Binding());
}
public static readonly DependencyProperty SelectedItemsProperty = DependencyProperty.Register(
"SelectedItems",
typeof(INotifyCollectionChanged),
typeof(XamDataGridSelectedItemsBehavior2),
new UIPropertyMetadata(new PropertyChangedCallback(OnSelectedItemsChanged)));
private static void OnSelectedItemsChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var behavior = obj as XamDataGridSelectedItemsBehavior;
if (behavior.SelectedItems != null)
CollectionChangedEventManager.RemoveListener(behavior.SelectedItems, behavior);
if (e.NewValue is INotifyCollectionChanged)
{
behavior.SelectedItems = e.NewValue as INotifyCollectionChanged;
CollectionChangedEventManager.AddListener(behavior.SelectedItems, behavior);
}
}
private INotifyCollectionChanged SelectedItems
{
get { return (INotifyCollectionChanged)GetValue(SelectedItemsProperty); }
set { SetValue(SelectedItemsProperty, value); }
}
<igDG:XamDataGrid DataSource="{Binding Passengers}">
<i:Interaction.Behaviors>
<b:XamDataGridSelectedItemsBehavior Path="Passengers.SelectedPassengers" />
</i:Interaction.Behaviors>
</igDG:XamDataGrid>
关于wpf - DataContext Change 不会更新 "Attached Behavior"中的 Binding,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13395201/
我不知道该怎么做... function f1() { var x = 10; function f2(fx) { var x; x = 6;
早期绑定(bind)和后期绑定(bind)有什么区别? 最佳答案 简短的回答是,早期(或静态)绑定(bind)是指编译时绑定(bind),后期(或动态)绑定(bind)是指运行时绑定(bind)(例如
如何在 SwiftUI View 上使用 Binding(get: { }, set: { }) 自定义绑定(bind)与 @Binding 属性。我已成功使用此自定义绑定(bind)与 @State
我经常发现自己遇到问题,即控件的两个(相关)值被更新,并且两者都会触发昂贵的操作,或者控件可能会暂时处于不一致的状态。 例如,考虑一个数据绑定(bind),其中两个值 (x,y) 相互减去,最终结果用
我想通过我的 ViewModel 控制我的一个窗口的高度和宽度。 这看起来很简单。 但没有。它不起作用。 它检查 ViewModel 的 Width但不是 Height . 奇怪的是,如果我切换 W
UI5中一次性绑定(bind)和单向绑定(bind)有什么区别? 是否有任何用户特定的用例我会使用它们? 我无法从文档中获得太多信息。 最佳答案 单程 它的作用:单向数据流。模型数据的变化(例如通过
(define make (lambda (x) (lambda (y) (cons x (list y))))) (let ((x 7) (p (make 4))) (cons
尽管我或多或少地了解什么是语言绑定(bind),但我很难理解它们是如何工作的。 例如,谁能解释一下如何为 WinAPI 制作 Java 绑定(bind)? 最佳答案 如果您搜索 Foreign Fun
谁能解释为什么我可以重新绑定(bind)列表但不能+? (binding [list vector] (list 1 3)) (binding [list +] (list 1 3)) (bi
我真的很喜欢 Caliburn 和命名约定绑定(bind),我很惊讶 可见性与“CanNAME”约定用于保护 Action 的方式不同。 据我所知, BooleanToVisibilityConver
我了解动态绑定(bind)的实现方式以及静态绑定(bind)和动态绑定(bind)之间的区别,但我只是无法理解动态绑定(bind)的定义。基本上它是一种运行时绑定(bind)类型。 最佳答案 基本上,
http://jsfiddle.net/3NRsd/ var foo = $("div").bind("click", function() { $("div").animate({"hei
这个问题我快疯了...我有一个用户控件,它有一个用于插入操作的 FormView 和一个用于所有其他操作的 GridView。 在这两个控件中,我都有一个 DropDownList,如下所示: '
我有一个绑定(bind)到 ListBox 的地址的 ObservableCollection。然后在 ItemTemplate 中,我使用 {Binding .} 绑定(bind)到当前地址记录。这
如果我有以下简单的 js/knockout 代码: .js( View 模型): var image = ko.observable('http://placehold.it/300x150'); 看
我正在 aurelia 上开发一个自定义属性,让用户在输入文本区域时从列表中进行选择。例如,用法将是这样的: 正如您可能注意到的,auto-complete是属性。现在,当我想显示提示时,我想在自定
我正在使用 EventEmitter2作为我的应用程序内部的消息总线。现在我需要绑定(bind)和取消绑定(bind)一些事件处理程序。因为我也希望他们bind将它们添加到给定的上下文中,我最终得到以
我有以下函数调用: $(".selector").on("click", callback.bind(this, param1, param2)); 在我的回调函数中,我想使用绑定(bind)的 th
我目前正在试验新的编译绑定(bind),并且(再次)达到了我在拼图中遗漏了一个小问题:为什么我必须调用 Bindings.Update?直到现在,我还认为实现 INotifyPropertyChang
我正在阅读一本关于编写 JavaScript 框架的书,并找到了这段代码。但是我不明白它是如何工作的,尤其是 bind.bind 的用法?有人知道吗? var bind = Function.prot
我是一名优秀的程序员,十分优秀!