2014-09-02 32 views
0

我想允許我的用戶通過導入XAML在我的程序中定義其控件。添加控件以在運行時使用用戶定義的XAML

作爲一個簡單的例子,假設用戶想要添加一個網格,他們可以導入下面的XAML。我如何得到這個添加到論壇。

<Grid 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Grid.Row="3" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="1,2,1,2"> 
    <Grid.RowDefinitions> 
     <RowDefinition Height="60" /> 
     <RowDefinition Height="60" /> 
    </Grid.RowDefinitions> 
    <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="45" /> 
     <ColumnDefinition Width="45" /> 
     <ColumnDefinition Width="45" /> 
     <ColumnDefinition Width="45" /> 
     <ColumnDefinition Width="45" /> 
    </Grid.ColumnDefinitions> 

    <Button Grid.Row="0" Grid.Column="0" Content="1" FontSize="14" Margin="1,2,1,2" FontWeight="Bold" />  
</Grid> 
+0

http://stackoverflow.com/questions/9021677/loading-xaml-at-runtime-using-the-mvvm-pattern-in-wpf – Donal 2014-09-02 15:57:16

回答

-1

首先讓我們創建一個AttachedProperty使加載的XAML可以添加到任何我們想要的面板或ContentControl中。

public class MyFrameworkObject : DependencyObject 
{ 
    public static readonly DependencyProperty RuntimeFrameWorkElementProperty = DependencyProperty.RegisterAttached("RuntimeFrameWorkElement", typeof(FrameworkElement), typeof(MyFrameworkObject),new PropertyMetadata(new PropertyChangedCallback(OnPropertyChanged))); 


    static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     SetRuntimeFrameWorkElement(d as UIElement, e.NewValue as FrameworkElement); 
    } 
    public static void SetRuntimeFrameWorkElement(UIElement element, FrameworkElement value) 
    { 
     if (element!=null && value != null && value.Parent == null) //The loaded Control can be added to only one Control because its Parent will be set 
     { 
      var panel = element as Panel; 
      if (panel != null) 
      { 
       panel.Children.Add(value); 
       return; 
      } 
      var contentControl = element as ContentControl; 
      if (contentControl != null) 
       contentControl.Content = value; 

      //TODO:ItemsControl 
     } 
    } 
} 

視圖模型:在視圖模型允許創建和負載可能被綁定到上述附加屬性的屬性。

public class ViewModel : INotifyPropertyChanged 
{ 
    public ViewModel() 
    { 
     LoadXaml(); 
    } 

    FrameworkElement frameWorkElement; 

    public FrameworkElement RuntimeFrameWorkElement 
    { 
     get { return frameWorkElement; } 
     set { frameWorkElement = value; OnPropertyChanged("RuntimeFrameWorkElement"); } 
    } 

    public void LoadXaml() 
    { 
     FileInfo f = new FileInfo(@"F:\myxaml.txt"); //Load xaml from some external file 
     if (f.Exists) 
      using (var stream = f.OpenRead()) 
      { 
       this.RuntimeFrameWorkElement = XamlReader.Load(stream) as FrameworkElement; 
      } 
    } 

    void OnPropertyChanged(string propName) 
    { 
     if (PropertyChanged != null) 
      PropertyChanged(this, new PropertyChangedEventArgs(propName)); 
    } 
    public event PropertyChangedEventHandler PropertyChanged; 
} 

xaml.cs

public MainWindow() 
    { 
     InitializeComponent(); 
     DataContext = new ViewModel(); 
    } 

XAML可以使用attachedProperty

<Window x:Class="StackoverflowQues.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:StackoverflowQues" 
    Title="MainWindow" Height="350" Width="525"> 
<StackPanel> 
    <Button Content="Ok"/> 
    <Grid local:MyFrameworkObject.RuntimeFrameWorkElement="{Binding RuntimeFrameWorkElement}"></Grid> 
</StackPanel> 

Simillarly可以綁定這個attachedproperty像棧,碼頭,收任何小組,電網

<StackPanel local:MyFrameworkObject.RuntimeFrameWorkElement="{Binding RuntimeFrameWorkElement}"> 
</StackPanel> 

或者也可以綁定到ContentControl中或ItemsControl的(尚未這樣做)

輸出我已經使用相同的xaml作爲你的

enter image description here

注意:如果您將此附加屬性指定給兩個或更多面板或控件,它將僅添加到第一個面板或控件。因爲那麼加載的xaml控件的Parent將被設置,並且不會添加另一個控件。

+0

-1。這違背了WPF中所有已知的,被接受的,推薦的,既定的良好實踐。 – 2014-09-02 21:53:50

1

這是一個類似於倫理邏輯答案的答案,但我不喜歡他在他的ViewModel中引用FrameworkElement。這將您的ViewModel與WPF結合在一起。相反,我會將用戶的內容加載到ViewModel中的字符串屬性中。

視圖模型

public string DynamicXaml 
{ 
    get { return _dynamicXaml; } 
    set 
    { 
     if (_dynamicXaml != value) 
     { 
      _dynamicXaml = value; 
      RaisePropertyChanged(() => DynamicXaml); 
     } 
    } 
} 

然後創建一個轉換器,將字符串轉換爲FrameworkElement的。

轉換

[ValueConversion(typeof(string), typeof(FrameworkElement))] 
public class XamlStringConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     FrameworkElement result = null; 
     string xaml = value as string; 
     if (!string.IsNullOrEmpty(xaml)) 
     { 
      try 
      { 
       result = XamlReader.Parse(xaml) as FrameworkElement; 
      } 
      catch (Exception ex) 
      { 
       //add logging logic here. 
      } 
     } 
     return result; 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     return null; 
    } 
} 

最後,你可以使用一個ContentControl中或ContentPresenter顯示自定義XAML。

XAML

<ContentControl x:Name="DynamicControl" 
       Content="{Binding Path=DynamicXaml, Converter={StaticResource XamlConverter}}"/> 
相關問題