簡體   English   中英

使用WPF中的MVVM模式在運行時加載XAML

[英]Loading XAML at runtime using the MVVM pattern in WPF

這是一個從最初發布的問題擴展到的問題: 鏈接到loading-xaml到運行時

我正在開發一個WPF MVVM應用程序,它從外部源動態加載XAML內容,與上面帖子中的答案非常相似。
這是我到目前為止所得到的:

  1. My View將ViewModel的實例聲明為資源,並創建該ViewModel的實例
  2. 在我的ViewModel構造函數中,我正在加載來自外部源(文件或數據庫..)的XamlString屬性。
  3. 在我看來,我有一個用戶在ViewModel完成加載后點擊的按鈕,在點擊事件代碼隱藏中,我將動態加載的XAML反序列化並將其添加到我的網格中。

我的問題是,如何消除代碼隱藏和自動化邏輯,以便View可以在ViewModel完成獲取XAML內容並初始化字符串屬性后立即動態呈現新的xaml部分?

我應該使用某種消息總線,以便ViewModel在設置屬性后通知,以便View可以添加新內容嗎?

讓我感到困擾的是,ViewModel確實有對Views的引用,不應該負責生成UI元素。

提前致謝!

編輯 :只是為了澄清:在我的特定情況下,我不是試圖將業務對象或集合(模型)綁定到UI元素(例如網格),這顯然可以通過模板和綁定來完成。 我的ViewModel正在從外部源檢索整個XAML表單,並將其設置為View可用的字符串屬性。

我的問題是:誰應該負責將這個XAML字符串屬性反序列化為UI元素,並在設置VM中的Xaml字符串屬性后以編程方式將其添加到我的網格中?
這聽起來更像是一個View責任,而不是ViewModel。 但我理解的模式強制要用V-VM綁定替換任何代碼隱藏邏輯。

我現在有一個有效的解決方案,我想分享它。 不幸的是,我沒有完全擺脫代碼隱藏,但它的工作方式與我期望的一樣。 以下是它的工作原理(簡化):

我有簡化的ViewModel:

public class MyViewModel : ViewModelBase
{
   //This property implements INPC and triggers notification on Set
   public string XamlViewData {get;set;}

   public ViewModel()
   {
      GetXamlFormData();  
   }

   //Gets the XAML Form from an external source (e.g. Database, File System)
   public void GetXamlFormData()
   {
       //Set the Xaml String property
       XamlViewData = //Logic to get XAML string from external source
   }
}

現在我的觀點:

<UserControl.Resources>
<ViewModel:MyViewModel x:Key="Model"></ViewModel:MyViewModel>
</UserControl.Resources>
<Grid DataContext="{StaticResource Model}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <StackPanel>
    <!-- This is the Grid used as a Place Holder to populate the dynamic content!-->
    <Grid x:Name="content" Grid.Row="1" Margin="2"/>
    <!-- Then create a Hidden TextBlock bound to my XamlString property. Right after binding happens I will trigger an event handled in the code-behind -->
    <TextBlock Name="tb_XamlString" Text="{Binding Path=XamlViewData, Mode=TwoWay, UpdateSourceTrigger=LostFocus, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" Visibility="Hidden" Loaded="tb_XamlString_Loaded" />
    </StackPanel>
</Grid>

基本上我在ViewModel中創建了一個綁定到我的XAML String屬性的隱藏TextBlock,並將其Loaded事件掛鈎到View后面代碼中的事件處理程序:

    private void tb_XamlString_Loaded(object sender, RoutedEventArgs routedEventArgs)
    {
        //First get the ViewModel from DataContext
        MyViewModel vm = content.DataContext as MyViewModel;
        FrameworkElement rootObject = XamlReader.Parse(vm.XamlViewData) as FrameworkElement;
        //Add the XAML portion to the Grid content to render the XAML form dynamically!
        content.Children.Add(rootObject);
    }

這可能不是最優雅的,但可以完成工作。 就像有人說的那樣,在MVVM中有一些像這樣的情況需要很少的代碼隱藏代碼。 在使用VM檢索和填充XamlString屬性並將其暴露給View時,此解決方案的一部分仍然使用V-VM Binding原則。 如果我們想單元測試XAML解析和加載功能,我們可以將它委托給一個單獨的類。

我希望有人覺得這很有用!

我無法理解你在說什么,所以我的答案將基於我的解釋。 您應該考慮發布您正在嘗試做的樣本(簡化)。

1)我認為你誤解了MVVM的作用。 MVVM主要是基於綁定的模式。 您的視圖模型應該公開包含業務對象的屬性,並且您的視圖應該只綁定到這些屬性。 如果我誤解了你,那就是你在做什么,那么你的問題就是你的視圖需要知道屬性何時更新(在反序列化你的xaml等之后)。 有兩種方法可以執行此操作:viewmodel上的INotifyPropertyChanged接口,或使視圖模型繼承自DependencyObject ,並創建屬性依賴項屬性。 我不會在這里詳細介紹,因為這是一個很大的主題,您應該在做出決定之前在Google上進行研究。

2)一般來說,如果您使用的是MVVM,則不應在視圖中使用click事件。 相反,在ICommand類型的視圖模型上創建屬性(並創建要匹配的ICommand實現,或使用DelegateCommand (谷歌)的實現,這將允許您使用委托來實現接口。想法是,您的視圖綁定到屬性並直接在viewmodel中執行處理程序。

3)如果要將信息從視圖模型推送到視圖,那么您應該在視圖模型上創建一個事件並在視圖中訂閱它,但這是最后的手段,僅用於顯示新窗口的情況等等。一般來說,你應該使用綁定。

4)為了更具體地說明你正在做什么,你應該將Grid的ItemsSource屬性綁定到視圖模型上的某個屬性。 注意,如果您希望能夠添加項目並獲得即時更新,則視圖模型上的屬性應為ObservableCollection<T>類型。

希望這可以幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM