简体   繁体   English

使用用户定义的XAML在运行时添加控件以形成表单

[英]Adding controls to form at runtime with user defined XAML

I want to allow my users to define their controls in my program by importing XAML. 我想允许我的用户通过导入XAML在我的程序中定义他们的控件。

As a simple example let say the user wanted to add a grid they could import the XAML below. 举一个简单的例子,假设用户想要添加一个网格,他们可以在下面导入XAML。 How do I get this added to the forum. 我如何将其添加到论坛中。

<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>

This is a similar answer to ethicallogics answer but I don't like that he references FrameworkElement in his ViewModel. 这是与道德逻辑答案类似的答案,但我不喜欢他在自己的ViewModel中引用FrameworkElement。 This couples your ViewModel to WPF. 这将您的ViewModel耦合到WPF。 Instead, I'd load the content from the user into a string property in your ViewModel. 相反,我会将用户的内容加载到ViewModel中的字符串属性中。

ViewModel 视图模型

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

Then create a converter to convert a string to a FrameworkElement. 然后创建一个转换器,将字符串转换为FrameworkElement。

Converter 转换器

[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;
    }
}

And finally you can use a ContentControl or ContentPresenter to display the custom xaml. 最后,您可以使用ContentControl或ContentPresenter来显示自定义xaml。

XAML XAML

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

First Lets create an AttachedProperty so that the loaded xaml could be added to any Panel or ContentControl we want. 首先,我们创建一个AttachedProperty,以便可以将加载的xaml添加到我们想要的任何Panel或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
        }
    }
}

ViewModel :In ViewModel lets Create and load a property that could be binded to above attached property. ViewModel:在ViewModel中,可以创建并加载可以绑定到上述附加属性的属性。

    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 xaml.cs

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

xaml lets use the attachedProperty 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 you can bind this attachedproperty to any Panel like Stack,Dock,Wrap ,Grid 类似地,您可以将此附加属性绑定到任何面板,如Stack,Dock,Wrap,Grid

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

Or can also bind to ContentControl or ItemsControl(yet to do) 或者也可以绑定到ContentControl或ItemsControl(尚未完成)

Output i have used same xaml as yours 输出我使用了与您相同的xaml

在此处输入图片说明

Note: If you specify this attached property to two or more panels or controls it will be added to the first one only . 注意:如果您将此附加属性指定给两个或多个面板或控件,则它将仅添加到第一个。 Because then the Parent of the Loaded xaml control will be set and wont be able to add another control. 因为那样,将设置“已加载xaml”控件的“父级”,因此将无法添加另一个控件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM