【问题标题】:Creating control instance in viewmodel在视图模型中创建控件实例
【发布时间】:2015-02-09 20:11:54
【问题描述】:

在我的程序中,我试图设计一个输出窗口来显示日志记录信息。目前,我正在绑定到一个字符串,并在日志数据进入后立即更新信息。

例如:

<FlowDocument>
    <Paragraph>
        <Run DataContext="{Binding}" Text="{Binding OutputText}"/>
    </Paragraph>
</FlowDocument>

(流文档在富文本框中)

在我的视图模型中,这就是我更新输出文本的方式

OutputText += loggingInfoString; 

问题是字符串是不可变的数据类型,我不喜欢每次新数据进入时都创建一个新字符串的想法。这是不必要的开销。

RichTextBox 有一个名为 AppendText 的方法,我假设它会使用类似 Stringbuilder 的东西来添加到字符串中。我遇到的问题是能够访问 ViewModel 中的 AppendText。

我想在 ViewModel 中创建一个 RichTextBox 实例并将 RichTextBox 绑定到 ViewModel 中的实例。我认为这违反了 MVVM,但我不完全确定。还有其他方法可以解决这个问题吗?还是应该只在 ViewModel 中创建 RichTextBox 实例?

例如: // 视图模型

RichTextBox Output;

// 更新方法

Output.AppendText(loggerInfoText);

// Xaml

<RichTextBox DataContext="{Binding Output}" />

提前致谢!

【问题讨论】:

    标签: c# wpf mvvm richtextbox viewmodel


    【解决方案1】:

    你创建一个这样的界面

    interface IAppender{
        void Append(string appendText);
    }
    

    您将此接口注入您的视图模型并在您的视图中实现它。 这意味着在您的视图模型代码中您只需执行

    appender.Append(loggerInfoText);
    

    在您看来,您通过向富文本添加文本来实现接口。 根据您的视图和视图模型的实现,根据创建视图模型的位置,您可以在视图模型中注入接口。假设视图模型是在视图中创建的,您会得到如下内容:

    class View : UserControl, IAppender{
        View(){
           InitializeComponent();
           DataContext = new YourViewModel(this);
        }
        void Append(string appendText){
            //add text to richttext
        }
    }
    
    public class YourViewModel : ViewModelBase{
       private IAppender _appender;
       public YourViewModel(IAppender appender){
           _appender = appender;
       }
    }
    

    我希望这应该让你开始 请注意,这是伪代码,未经测试甚至没有通过编译器运行它。 将richttext 传递给viewmodel 确实不是MVVM。这个想法是关注点分离。通过我的接口方法,这没有被违反。

    【讨论】:

    • 太棒了!感谢您的迅速回复。我不敢相信我什至没有想到这一点。有时,作为程序员,我们会忽略最简单的事情。再次感谢:)
    • 我有一个简单的问题。如果我的视图模型中有一个事件处理程序。将附加文本添加到视图模型的事件处理程序中会是不好的做法吗?
    • 取决于你的意思。从注入到视图模型的接口调用 Append,并在事件处理程序中执行此操作,这不是问题。事件处理程序本身不应该附加到 WPF 组件事件。为此使用命令,甚至可能使用行为。但这已经是更高级的 MVVM 了。很难看出你需要什么;-)
    • 我的意思是在我的 MainWindowViewModel 中有一个名为 UpdateEvent 的 EventHandler。如果在我的 MainWindow (视图)中我将附加方法附加到此事件处理程序将是不好的。例如:(伪代码)Constructor() { DataContext = new MainWindowViewModel(); DataContext.UpdateEvent += 追加; } void Append(object sender, EventArgs e) { // 做附加 }
    • 整个想法是通过可以通过视图模型的构造函数或绑定注入的接口在视图和视图模型之间建立依赖关系。所以我不会选择你的事件处理程序解决方案。但最终取决于您。
    【解决方案2】:

    作为注入依赖项的替代方法(即使解耦,也使得注入视图很麻烦),由您的控件实现:

    首先,您可以在内部使用 StringBuilder 来构建您的字符串。

    public class MyViewModel : ViewModelBase 
    {
        private StringBuilder sb;
        private string Content 
        {
            return sb.ToString();
        }
        protected Append(string text) {
            sb.Append(text);
            PropertyChanged("Content");
        }
    }
    

    然后将Content 属性绑定到您的XAML 元素。这样,您的 ViewModel 中将不会有分配/解除分配,并且您不需要麻烦 DI 将您的 View/Control 注入 ViewModel(即使它是由接口抽象的)。

    但是,这假设您将在任何地方都避免这种分配。如果您只希望它在某种类型的应用程序(即 Windows 应用商店应用程序)中而不是在您的 ASP.NET 或桌面应用程序中,则第二种选择可能更具吸引力(尤其是对于其他类似场景):

    您可以使用自定义行为来实现此目的,如this question 中的答案所示。行为是可重用的,您不必在 ViewModel 中做这种决定,允许您以不同的方式处理每个平台甚至每个视图。

    而且它简化了您的 ViewModel,您不必费心如何将实现 IAppender 的 View 注入到您的 ViewModel 中(在 View-first 方法中可能会导致问题,其中 View 由 IoC 解决,而获取注入到视图中的 ViewModel 设置为 DataContext)

    【讨论】:

    • 如果我使用 StringBuilder 与使用字符串没有什么不同,实际上会更糟。每次我附加字符串生成器时,我都会将其转换为字符串。所以基本上我会附加一个字符串生成器,然后生成一个字符串,这正是我想要避免的。
    猜你喜欢
    • 2011-12-20
    • 1970-01-01
    • 1970-01-01
    • 2021-05-21
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 2020-10-09
    • 1970-01-01
    相关资源
    最近更新 更多