如何保证,视图模型属性更改它再次值之前已经绑定的看法? [英] How to ensure, ViewModel property is bound already on view before changing it value again?
问题描述
有是继案例:
视图模型
有一个对象,它的变化非常快。 (通过不同的线程)
There is following case:
ViewModel
has an object which changes very fast. (via different threads)
查看
通过 NotifyPropertyChanged
界面,但它似乎它的工作原理减缓和查看之前绑定新的价值和借鉴它,然后它改变多次因此错过一些值。
View
gets informed via NotifyPropertyChanged
interface but it seems it works to slow and before View bind new value and draw it then it changes more times therefore It misses some values.
我也试图绑定查看
来排队,然后视图模型
能排队
这和<$ C 。$ C>查看可通过出队吸取
I also tried to bind View
to queue then ViewModel
could Enqueue
it and View
could draw via dequeueing.
不幸的是另外一个问题出现了: RaisePropertyChanged(()=>之后;队列);
查看
未告知,它被改变。
Unfortunately another problem occurred: after RaisePropertyChanged(() => queue);
View
is not informed that it was changed.
在这种情况下,在 INotifyPropertyChanged的
接口的实现并没有奏效。
In such case the implementation of the INotifyPropertyChanged
interface did not worked.
你有什么想法?
示例代码视图模型
:
public class ExamplaryViewModel
{
public ExamplaryViewModel()
{
Messenger.Default.Register<NotificationMessage<Message>>(this, m => ProcessNotificationMessage(m.Content));
}
public void ProcessNotificationMessage(Message message)
{
MessageOftenBeingChanged = message;
RaisePropertyChanged(() => MessageOftenBeingChanged );
}
}
查看
结合 MessageOftenBeingChanged
另一种选择是在意见建议准备快照:
Another option would be to prepare snapshot as was suggested in comments:
public void ProcessNotificationMessage(Message message)
{
Messages.Enqueue(message);
RaisePropertyChanged(() => Messages);
}
查看
:
<controls:RichTextBoxMonitor Messages="{Binding Messages}
控制
:
public class BindableRichTextBox : RichTextBox
{
public static readonly DependencyProperty MessagesProperty = DependencyProperty.Register("Messages",
typeof(ConcurrentQueue<Message>), typeof(BindableRichTextBox ), new FrameworkPropertyMetadata(null, OnQueueChangedChanged));
public ConcurrentQueue<Message> CyclicMessages
{
get { return (ConcurrentQueue<Message>)GetValue(MessagesProperty ); }
set { SetValue(MessagesProperty , value); }
但随后,不幸的是, RaisePropertyChanged()
方法不会触发的变化发生了。
but then, unfortunately the RaisePropertyChanged()
method does not trigger that changes happened.
我计划在事件控制 OnQueueChangedChanged
尝试出队,只是画的项目,如段落新的内联。
I planned in control in event OnQueueChangedChanged
try dequeueing and just draw items as new Inlines for Paragraph.
推荐答案
您可以实现生产者 - 消费者。
看这简化版本。
-
RunProducer
仅用于测试,你的情况ProcessNotificationMessage
将以类似的方式工作。 -
RunConsumer
是不断检查新邮件的方法和集消息
有一些延迟,否则用户将无法读取它。 - 这概念只是一个快速的证明,但你可以通过提供一个方法
ShowNextMessage
和<$ C更好地落实它,例如$ C> IsMessageAvailable ,然后即可显示一个新的消息,并要求它时,认为可以决定。这将是一个更好的设计。即使用户可以隐藏一些消息的速度,然后,你只需要绑定ShowNextMessage
到点击
事件。
RunProducer
is only for tests, in your caseProcessNotificationMessage
will work in a similar way.RunConsumer
is a method which constantly checks for new messages and setsMessage
with some delay, otherwise a user wouldn't be able to read it.- It's just a quick proof of concept, but you could implement it better, for example by providing a methods
ShowNextMessage
andIsMessageAvailable
, then the view could decide when is ready to display a new message and request for it. It would be a better design. Even a user could hide some messages faster then, you'd need only to bindShowNextMessage
toClick
event. Full source code
public class MyViewModel : INotifyPropertyChanged
{
public ConcurrentQueue<string> Queue { get; set; }
#region Message
private string _message;
public string Message
{
get
{
return _message;
}
set
{
if (_message != value)
{
_message = value;
OnPropertyChanged();
}
}
}
#endregion
public MyViewModel()
{
Queue = new ConcurrentQueue<string>();
RunProducer();
RunConsumer();
}
public void RunProducer()
{
Task.Run(() =>
{
int i = 0;
while (true)
{
if (Queue.Count < 10)
Queue.Enqueue("TestTest " + (i++).ToString());
else
Task.Delay(500).Wait();
}
});
}
public void RunConsumer()
{
Task.Run(() =>
{
while (true)
{
if (Queue.Count > 0)
{
string msg = "";
if (Queue.TryDequeue(out msg))
Message = msg;
}
else
{
Task.Delay(500).Wait();
}
Task.Delay(100).Wait();
}
});
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
在空队列的情况下,你可以使用 ManualResetMonitor
,以避免不必要的迭代。
In case of empty queue you could use ManualResetMonitor
to avoid unnecessary iterations.
说明你的代码:结果
。如果集合可再改结合的目的,你应该只<$使用C $ C>的ObservableCollection< T> (或者一些工具 INotifyCollectionChanged
),因为它跟踪的变化和不重装所有的东西。
Remarks to your code:
If a collection can be changed then for binding purpose you should use only ObservableCollection<T>
(or something that implements INotifyCollectionChanged
), because it tracks changes and doesn't reload everything.
然而,在你的代码的整体结合应该被刷新(如你通知整个集合已更改),但我认为这个机制是聪明的,如果检查引用是否相等如果这样的话没有发生刷新。大概HAX将其设置为空
和背部会刷新: - )
However in your code a whole binding should be refreshed (as you notified that whole collection has been changed), but I think this mechanism is smarter and checks if references are equal, if so then no refresh occurs. Probably a hax to set it to null
and back would refresh it :-).
这篇关于如何保证,视图模型属性更改它再次值之前已经绑定的看法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!