在视图模型中创建控件实例

Creating control instance in viewmodel

在我的程序中,我试图设计一个输出 window 来显示日志信息。现在,我绑定到一个字符串并在日志数据输入时立即更新信息。

例如:

<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}" />

提前致谢!

你像这样创建一个界面

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。这个想法是分离关注点。通过我的界面方法,这没有被违反。

作为由您的控件实现的注入依赖项的替代方法(即使分离,也会使视图注入变得很痛苦):

首先,您可以在内部使用 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 中就不会有 allocations/deallocations,你也不需要麻烦 DI 将你的 View/Control 注入 ViewModel(即使它是由接口抽象的)。

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

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

并且它简化了您的 ViewModel,您将不必费心如何将实现 IAppender 的 View 注入到您的 ViewModel 中(这可能会在 View-first 方法中引起麻烦,其中 View 由IoC 并获取注入到视图中的 ViewModel 以设置为 DataContext)