WPF,可使用 MVVM (Caliburn.Micro) 绑定 richTextBox - 文档已属于另一个 RichTextBox
我有一个非常有趣的问题。我在 WPF 应用程序中使用此技术:Caliburn.Micro 和 MEF。
我从视图模型打开新窗口(不是屏幕)。效果很好。
在 Init-View-Model 中,我有这个方法,它打开新的 WPF 窗口,而不是 shell 中的屏幕。
...
public IEnumerable<IResult> Send()
{
yield return new ShowWindow("NewScreen")
.InitializeWith(_service.DetailData(Account,_selectedFriend.Key));
}
...
ShowWindow 类如下所示:
public class ShowWindow : IResult
{
readonly Type _windowType;
readonly string _name;
[Import]
public IShellViewModel Shell { get; set; }
Action<object> _initializationAction = window => { };
public ShowWindow InitializeWith<T>(T argument)
{
_initializationAction = window =>
{
var initializable = window as IInitializable<T>;
if (initializable != null)
initializable.Initialize(argument);
};
return this;
}
public ShowWindow(string name)
{
_name = name;
}
public ShowWindow(Type windowType)
{
_windowType = windowType;
}
public void Execute(ActionExecutionContext context)
{
var window = !string.IsNullOrEmpty(_name)
? IoC.Get<object>(_name)
: IoC.GetInstance(_windowType, null);
_initializationAction(window);
IoC.Get<IWindowManager>().Show(window);
Completed(this, new ResultCompletionEventArgs());
}
public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };
public static ShowWindow Of<T>()
{
return new ShowWindow(typeof(T));
}
}
我使用事件聚合器将消息从初始化视图模型发送到新窗口。
一切都运行良好,直到我添加了 richtebox 控件。我需要可绑定的富文本框。所以我
使用Jason Mueller的可绑定版本(http://social.msdn.microsoft.com/forums/en-US/wpf/thread/f77c011a-0aba-449f-b6f4-920e58ebf997/)
New-View-Model 如下所示:
public class NewViewModel : Screen, IInitializable<DetailData>, IHandle<string>
{
private IEventAggregator _eventAgg;
private FlowDocument _conversation;
//bind on document of richtextBox
public FlowDocument Conversation
{
get { return _conversation; }
set
{
_conversation = value;
NotifyOfPropertyChange("Conversation");
}
}
[ImportingConstructor]
public NewViewModel(IEventAggregator eventAgg)
{
_eventAgg = eventAgg;
_eventAgg.Subscribe(this);
**//I think problem is here
_conversation = new FlowDocument();**
}
public void Handle(string message)
{
Conversation.Blocks
.Add(new Paragraph(new Run(message)));
}
}
在 New-View-Model 类中,我将属性 Conversation 绑定到视图中的 RichTextBox 上。
视图:
<Controls:BindableRichTextBox Document="{Binding Path=Conversation, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
FontSize="13"
Margin="4,4,4,4"
Grid.Row="0" />
问题是。
我从 Init-View-Model 调用方法 public IEnumerable Send() ->它称为 New-View-Model -> 的构造函数它打开了新窗口。是的,
当我第二次调用方法 public IEnumerable Send() 时,我收到此错误: System.Argument.Exception {"Document 已经属于另一个 RichTextBox。"}
我在可绑定的 richTextBox 类中收到此错误。
...
protected override void OnInitialized(EventArgs e)
{
// Hook up to get notified when DocumentProperty changes.
DependencyPropertyDescriptor descriptor = DependencyPropertyDescriptor.FromProperty(DocumentProperty, typeof(BindableRichTextBox));
descriptor.AddValueChanged(this, delegate
{
// If the underlying value of the dependency property changes,
// update the underlying document, also.
**line 54: base.Document = (FlowDocument)GetValue(DocumentProperty);**
});
// By default, we support updates to the source when focus is lost (or, if the LostFocus
// trigger is specified explicity. We don't support the PropertyChanged trigger right now.
this.LostFocus += new RoutedEventHandler(BindableRichTextBox_LostFocus);
base.OnInitialized(e);
}
....
我认为问题是因为它只调用一次 New-View-Model 的构造函数。所以我调用了五次 Send 方法,但它只调用了一次 New-View-Model 的构造函数。 怎么解决?
StackTrace:
at System.Windows.Controls.RichTextBox.set_Document(FlowDocument value)
at Spirit.Controls.BindableRichTextBox.b__0(Object , EventArgs ) in C:\Users\Jan\Documents\Visual Studio 2010\Projects\C#\Pokec_Messenger\ver.beta\Pokec__Messenger\Spirit_v1.2\Controls\BindableRichTextBox.cs:line 54
at MS.Internal.ComponentModel.PropertyChangeTracker.OnPropertyInvalidation(DependencyObject d, DependencyPropertyChangedEventArgs args)
at System.Windows.DependentList.InvalidateDependents(DependencyObject source, DependencyPropertyChangedEventArgs sourceArgs)
at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
at System.Windows.DependencyObject.InvalidateProperty(DependencyProperty dp)
at System.Windows.Data.BindingExpressionBase.Invalidate(Boolean isASubPropertyChange)
at System.Windows.Data.BindingExpression.TransferValue(Object newValue, Boolean isASubPropertyChange)
at System.Windows.Data.BindingExpression.Activate(Object item)
at System.Windows.Data.BindingExpression.AttachToContext(AttachAttempt attempt)
at System.Windows.Data.BindingExpression.MS.Internal.Data.IDataBindEngineClient.AttachToContext(Boolean lastChance)
at MS.Internal.Data.DataBindEngine.Task.Run(Boolean lastChance)
at MS.Internal.Data.DataBindEngine.Run(Object arg)
at MS.Internal.Data.DataBindEngine.OnLayoutUpdated(Object sender, EventArgs e)
at System.Windows.ContextLayoutManager.fireLayoutUpdateEvent()
at System.Windows.ContextLayoutManager.UpdateLayout()
at System.Windows.ContextLayoutManager.UpdateLayoutCallback(Object arg)
at System.Windows.Media.MediaContext.InvokeOnRenderCallback.DoWork()
at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
at System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)
at System.Windows.Media.MediaContext.Resize(ICompositionTarget resizedCompositionTarget)
at System.Windows.Interop.HwndTarget.OnResize()
at System.Windows.Interop.HwndTarget.HandleMessage(WindowMessage msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Interop.HwndSource.HwndTargetFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
问题出在哪里,我尝试了很多方法,但都不起作用。感谢您的建议和帮助。我很无奈。
I have a very interesting problem. I use this technologies in WPF app: Caliburn.Micro and MEF.
I open new window (not screen) from view model. It works good.
In Init-View-Model I have this method, which open new WPF Window, not screen in shell.
...
public IEnumerable<IResult> Send()
{
yield return new ShowWindow("NewScreen")
.InitializeWith(_service.DetailData(Account,_selectedFriend.Key));
}
...
ShowWindow class look like this:
public class ShowWindow : IResult
{
readonly Type _windowType;
readonly string _name;
[Import]
public IShellViewModel Shell { get; set; }
Action<object> _initializationAction = window => { };
public ShowWindow InitializeWith<T>(T argument)
{
_initializationAction = window =>
{
var initializable = window as IInitializable<T>;
if (initializable != null)
initializable.Initialize(argument);
};
return this;
}
public ShowWindow(string name)
{
_name = name;
}
public ShowWindow(Type windowType)
{
_windowType = windowType;
}
public void Execute(ActionExecutionContext context)
{
var window = !string.IsNullOrEmpty(_name)
? IoC.Get<object>(_name)
: IoC.GetInstance(_windowType, null);
_initializationAction(window);
IoC.Get<IWindowManager>().Show(window);
Completed(this, new ResultCompletionEventArgs());
}
public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };
public static ShowWindow Of<T>()
{
return new ShowWindow(typeof(T));
}
}
I use event aggregator on sending messages from init view model to new window.
Everything worked well until I added richtebox control. I need bindable richtextbox. So I
use bindable version of Jason Mueller (http://social.msdn.microsoft.com/forums/en-US/wpf/thread/f77c011a-0aba-449f-b6f4-920e58ebf997/)
New-View-Model look like this:
public class NewViewModel : Screen, IInitializable<DetailData>, IHandle<string>
{
private IEventAggregator _eventAgg;
private FlowDocument _conversation;
//bind on document of richtextBox
public FlowDocument Conversation
{
get { return _conversation; }
set
{
_conversation = value;
NotifyOfPropertyChange("Conversation");
}
}
[ImportingConstructor]
public NewViewModel(IEventAggregator eventAgg)
{
_eventAgg = eventAgg;
_eventAgg.Subscribe(this);
**//I think problem is here
_conversation = new FlowDocument();**
}
public void Handle(string message)
{
Conversation.Blocks
.Add(new Paragraph(new Run(message)));
}
}
In New-View-Model class I bind property Conversation on RichTextBox in View.
View:
<Controls:BindableRichTextBox Document="{Binding Path=Conversation, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
FontSize="13"
Margin="4,4,4,4"
Grid.Row="0" />
Problem is.
I call method public IEnumerable Send() from Init-View-Model -> it called cotructor of New-View-Model -> and it opened new window. That is right
Than I call method public IEnumerable Send() second time and I get this error: System.Argument.Exception {"Document already belongs to another RichTextBox."}
This error I get in class of bindable richTextBox.
...
protected override void OnInitialized(EventArgs e)
{
// Hook up to get notified when DocumentProperty changes.
DependencyPropertyDescriptor descriptor = DependencyPropertyDescriptor.FromProperty(DocumentProperty, typeof(BindableRichTextBox));
descriptor.AddValueChanged(this, delegate
{
// If the underlying value of the dependency property changes,
// update the underlying document, also.
**line 54: base.Document = (FlowDocument)GetValue(DocumentProperty);**
});
// By default, we support updates to the source when focus is lost (or, if the LostFocus
// trigger is specified explicity. We don't support the PropertyChanged trigger right now.
this.LostFocus += new RoutedEventHandler(BindableRichTextBox_LostFocus);
base.OnInitialized(e);
}
....
I think problem is because it call only once constructor of New-View-Model. So I call five times method Send but it call constructor of New-View-Model only once.
How can solve it ?
StackTrace:
at System.Windows.Controls.RichTextBox.set_Document(FlowDocument value)
at Spirit.Controls.BindableRichTextBox.b__0(Object , EventArgs ) in C:\Users\Jan\Documents\Visual Studio 2010\Projects\C#\Pokec_Messenger\ver.beta\Pokec__Messenger\Spirit_v1.2\Controls\BindableRichTextBox.cs:line 54
at MS.Internal.ComponentModel.PropertyChangeTracker.OnPropertyInvalidation(DependencyObject d, DependencyPropertyChangedEventArgs args)
at System.Windows.DependentList.InvalidateDependents(DependencyObject source, DependencyPropertyChangedEventArgs sourceArgs)
at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
at System.Windows.DependencyObject.InvalidateProperty(DependencyProperty dp)
at System.Windows.Data.BindingExpressionBase.Invalidate(Boolean isASubPropertyChange)
at System.Windows.Data.BindingExpression.TransferValue(Object newValue, Boolean isASubPropertyChange)
at System.Windows.Data.BindingExpression.Activate(Object item)
at System.Windows.Data.BindingExpression.AttachToContext(AttachAttempt attempt)
at System.Windows.Data.BindingExpression.MS.Internal.Data.IDataBindEngineClient.AttachToContext(Boolean lastChance)
at MS.Internal.Data.DataBindEngine.Task.Run(Boolean lastChance)
at MS.Internal.Data.DataBindEngine.Run(Object arg)
at MS.Internal.Data.DataBindEngine.OnLayoutUpdated(Object sender, EventArgs e)
at System.Windows.ContextLayoutManager.fireLayoutUpdateEvent()
at System.Windows.ContextLayoutManager.UpdateLayout()
at System.Windows.ContextLayoutManager.UpdateLayoutCallback(Object arg)
at System.Windows.Media.MediaContext.InvokeOnRenderCallback.DoWork()
at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)
at System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)
at System.Windows.Media.MediaContext.Resize(ICompositionTarget resizedCompositionTarget)
at System.Windows.Interop.HwndTarget.OnResize()
at System.Windows.Interop.HwndTarget.HandleMessage(WindowMessage msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Interop.HwndSource.HwndTargetFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
Where is it problem, I try many ways but any doesn’t work. Thank for your advice and help. I am helpless.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我的解决方案:
我用[PartCreationPolicy(CreationPolicy.NonShared)]标记我的新视图模型,因为MEF导出的默认生命周期是共享的(单例)。
MY SOLUTION:
I mark my new view model with [PartCreationPolicy(CreationPolicy.NonShared)],because the default lifetime of MEF exports is Shared (singleton).