Make (create) reusable dynamic Views

后端 未结 1 1474
暖寄归人
暖寄归人 2020-12-06 18:17

Out team wants to create reusable, stylable Views. For example, we want to reuse in different apps the CommonPromptView (our own, customizable dialog box where

相关标签:
1条回答
  • 2020-12-06 18:40

    I think in your case you need to look in the direction of DataTemplate, to make the dynamic content. I made a few examples that show this. The general meaning of this examples:

    Given two buttons, one for the user and one for the administrator. If you choose the User, the content is displayed to the user, if the Admin, then for administrator.

    Clearly, this is not the most realistic example, but it's just a way to show the dynamic selection of content. For you, you will determine the condition of replacement content.

    Example A

    This example demonstrates the dynamic replacing DataTemplates, depending on the input values​​. If we think in terms of style patterns, it is very similar to the abstract factory, where instead of classes - DataTemplate, and factory method is a dynamic DataTemplate selector. Example is fully suitable for MVVM pattern. Below is an example:

    MainWindow.xaml

    <Grid>
        <ContentControl Name="MainView"
                        ContentTemplate="{StaticResource MainView}">
    
            <ViewModels:MainViewModel />
        </ContentControl>
    </Grid>
    

    MainView.xaml

    It's a DataTemplate in ResourceDictionary. There are two templates: UserTemplate and AdminTemplate. One for the user and one for the administrator. In the style of ContentControl defined ContentTemplateSelector, and a collection of templates that will be installed by the condition. In property Content for DynamicContentControl is set content string that can be: User or Admin.

    <DataTemplateSelectors:DynamicTemplateSelector x:Key="MyTemplateSelector" />
    
    <DataTemplate x:Key="UserTemplate">
        <StackPanel>
            <TextBlock Text="Content for user"
                       FontSize="20"
                       HorizontalAlignment="Center"
                       VerticalAlignment="Center" />
    
            <Button Content="Yes" />
            <Button Content="No" />
        </StackPanel>
    </DataTemplate>
    
    <DataTemplate x:Key="AdminTemplate">
        <StackPanel>
            <TextBlock Text="Content for admin"
                       FontSize="20"
                       HorizontalAlignment="Center"
                       VerticalAlignment="Center" />
    
            <TextBox Text="Whom banned?" />
            <Button Content="Ban" />
            <Button Content="AntiBan" />
        </StackPanel>
    </DataTemplate>
    
    <Style x:Key="DynamicContentStyle" TargetType="{x:Type ContentControl}">
        <Setter Property="ContentTemplateSelector" Value="{StaticResource MyTemplateSelector}" />
        <Setter Property="Width" Value="200" />
        <Setter Property="Height" Value="200" />
    
        <Setter Property="DataTemplateSelectors:DynamicTemplateSelector.Templates">
            <Setter.Value>
                <DataTemplateSelectors:TemplateCollection>
                    <DataTemplateSelectors:Template Value="User" 
                                                    DataTemplate="{StaticResource UserTemplate}" />
    
                    <DataTemplateSelectors:Template Value="Admin" 
                                                    DataTemplate="{StaticResource AdminTemplate}" />
                </DataTemplateSelectors:TemplateCollection>
            </Setter.Value>
        </Setter>
    </Style>
    
    <DataTemplate x:Key="MainView" DataType="{x:Type ViewModels:MainViewModel}">
        <Grid>
            <Button Name="UserButton"
                    Content="Are you user?"
                    Width="100"
                    Height="30"
                    HorizontalAlignment="Left"
                    VerticalAlignment="Top"
                    Command="{Binding UserButtonCommand}" />
    
            <Button Name="AdminButton"
                    Content="Are you admin?"
                    Width="100"
                    Height="30"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Top"
                    Command="{Binding AdminButtonCommand}" />
    
            <ContentControl Name="DynamicContent"                            
                            Style="{StaticResource DynamicContentStyle}"
                            Content="{Binding Path=MainModel.ContentType,
                                              Mode=TwoWay,
                                              UpdateSourceTrigger=PropertyChanged}" />
        </Grid>
    </DataTemplate>
    

    MainViewModel.cs

    Here in commands set the content type.

    public class MainViewModel
    {
        #region MainModel
    
        private MainModel _mainModel = null;
    
        public MainModel MainModel
        {
            get
            {
                return _mainModel;
            }
    
            set
            {
                _mainModel = value;
            }
        }
    
        #endregion
    
        #region UserButton Command
    
        private ICommand _userButtonCommand = null;
    
        public ICommand UserButtonCommand
        {
            get
            {
                if (_userButtonCommand == null)
                {
                    _userButtonCommand = new RelayCommand(param => this.UserButton(), null);
                }
    
                return _userButtonCommand;
            }
        }
    
        private void UserButton() 
        {
            MainModel.ContentType = "User";
        }
    
        #endregion
    
        #region AdminButton Command
    
        private ICommand _adminButtonCommand = null;
    
        public ICommand AdminButtonCommand
        {
            get
            {
                if (_adminButtonCommand == null)
                {
                    _adminButtonCommand = new RelayCommand(param => this.AdminButton(), null);
                }
    
                return _adminButtonCommand;
            }
        }
    
        private void AdminButton()
        {
            MainModel.ContentType = "Admin";
        }
    
        #endregion
    
        public MainViewModel() 
        {
            MainModel = new MainModel();
        }
    }
    

    MainModel.cs

    public class MainModel : NotificationObject
    {
        private string _contentType = "";
    
        public string ContentType
        {
            get
            {
                return _contentType;
            }
    
            set
            {
                _contentType = value;
                NotifyPropertyChanged("ContentType");
            }
        }
    }
    

    DynamicTemplateSelector

    Taken and little reworked from CodeProject:

    public class DynamicTemplateSelector : DataTemplateSelector
    {
        #region Templates Dependency Property
    
        public static readonly DependencyProperty TemplatesProperty =
            DependencyProperty.RegisterAttached("Templates", typeof(TemplateCollection), typeof(DataTemplateSelector),
            new FrameworkPropertyMetadata(new TemplateCollection(), FrameworkPropertyMetadataOptions.Inherits));
    
        public static TemplateCollection GetTemplates(UIElement element)
        {
            return (TemplateCollection)element.GetValue(TemplatesProperty);
        }
    
        public static void SetTemplates(UIElement element, TemplateCollection collection)
        {
            element.SetValue(TemplatesProperty, collection);
        }
    
        #endregion
    
        #region SelectTemplate
    
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            string myStringItem = (string)item;
    
            if (!(container is UIElement))
            {
                return base.SelectTemplate(item, container);
            }
    
            TemplateCollection templates = GetTemplates(container as UIElement);
    
            if (templates == null || templates.Count == 0)
            {
                base.SelectTemplate(item, container);
            }
    
            foreach (var template in templates)
            {
                if (myStringItem.Equals(template.Value.ToString()))
                {
                    return template.DataTemplate;
                }
            }
    
            return base.SelectTemplate(item, container);
        }
    
        #endregion
    }
    
    #region TemplateCollection
    
    public class TemplateCollection : List<Template>
    {
    
    }
    
    #endregion
    
    #region Template Dependency Object
    
    public class Template : DependencyObject
    {
        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(string), typeof(Template));
    
        public static readonly DependencyProperty DataTemplateProperty =
           DependencyProperty.Register("DataTemplate", typeof(DataTemplate), typeof(Template));
    
        public string Value
        { get { return (string)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } }
    
        public DataTemplate DataTemplate
        { get { return (DataTemplate)GetValue(DataTemplateProperty); } set { SetValue(DataTemplateProperty, value); } }
    }
    
    #endregion
    

    Result for User

    Result for user

    Result for Admin

    Result for admin

    Notes about this way

    Advantages:

    • full independence from different View

    Disadvantages:

    • need to create a separate data template (View), in some cases ViewModel, for each condition.

    Conclusion:

    This method is suitable for completely different views, if the representations are not much different, you see a second example.

    Example B

    This example uses a one DataTemplate, the data is taken from the model, by default, all controls are hidden (Visibility.Collapsed), all actions in View are performed via DataTriggers. Example is fully suitable for MVVM pattern.

    MainWindow.xaml

    <Grid>
        <ContentControl Name="MainView"
                        ContentTemplate="{StaticResource MainView}">
    
            <ViewModels:MainViewModel />
        </ContentControl>
    </Grid>
    

    MainView.xaml

    <DataTemplate x:Key="MainView" DataType="{x:Type ViewModels:MainViewModel}">
        <Grid>
            <Button Name="UserButton"
                    Content="Are you user?"
                    Width="100"
                    Height="30"
                    HorizontalAlignment="Left"
                    VerticalAlignment="Top"
                    Command="{Binding UserButtonCommand}" />
    
            <Button Name="AdminButton"
                    Content="Are you admin?"
                    Width="100"
                    Height="30"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Top"
                    Command="{Binding AdminButtonCommand}" />
    
            <StackPanel Name="MainViewPanel"
                        Tag="{Binding Path=MainModel.ContentType,
                                      Mode=TwoWay, 
                                      UpdateSourceTrigger=PropertyChanged}">
    
                <TextBlock Name="TitleTextBlock"
                           Text="{Binding Path=MainModel.TitleText,
                                          Mode=TwoWay, 
                                          UpdateSourceTrigger=PropertyChanged}"
                           FontSize="20"
                           HorizontalAlignment="Center"
                           VerticalAlignment="Center"
                           Visibility="Collapsed" />
    
                <TextBox Name="BannedTextBlock"
                         Text="{Binding Path=MainModel.BannedName,
                                        Mode=TwoWay, 
                                        UpdateSourceTrigger=PropertyChanged}"
                         Visibility="Collapsed" />
    
                <Button Name="YesButton" 
                        Content="{Binding Path=MainModel.ContentYesButton,
                                          Mode=TwoWay, 
                                          UpdateSourceTrigger=PropertyChanged}" 
                        Visibility="Collapsed" />
    
                <Button Name="NoButton" 
                        Content="{Binding Path=MainModel.ContentNoButton,
                                          Mode=TwoWay, 
                                          UpdateSourceTrigger=PropertyChanged}" 
                        Visibility="Collapsed" />
            </StackPanel>
        </Grid>
    
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding ElementName=MainViewPanel, Path=Tag}" Value="User">
                <Setter TargetName="TitleTextBlock" Property="Visibility" Value="Visible" />
                <Setter TargetName="YesButton" Property="Visibility" Value="Visible" />
            </DataTrigger>
    
            <DataTrigger Binding="{Binding ElementName=MainViewPanel, Path=Tag}" Value="Admin">
                <Setter TargetName="TitleTextBlock" Property="Visibility" Value="Visible" />
                <Setter TargetName="BannedTextBlock" Property="Visibility" Value="Visible" />
                <Setter TargetName="YesButton" Property="Visibility" Value="Visible" />
                <Setter TargetName="NoButton" Property="Visibility" Value="Visible" />
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>
    

    MainViewModel.cs

    public class MainViewModel
    {
        #region MainModel
    
        private MainModel _mainModel = null;
    
        public MainModel MainModel
        {
            get
            {
                return _mainModel;
            }
    
            set
            {
                _mainModel = value;
            }
        }
    
        #endregion
    
        #region UserButton Command
    
        private ICommand _userButtonCommand = null;
    
        public ICommand UserButtonCommand
        {
            get
            {
                if (_userButtonCommand == null)
                {
                    _userButtonCommand = new RelayCommand(param => this.UserButton(), null);
                }
    
                return _userButtonCommand;
            }
        }
    
        private void UserButton() 
        {
            MainModel.ContentType = "User";
            MainModel.TitleText = "Hello User!";
            MainModel.ContentYesButton = "Yes";
            MainModel.ContentNoButton = "No";
        }
    
        #endregion
    
        #region AdminButton Command
    
        private ICommand _adminButtonCommand = null;
    
        public ICommand AdminButtonCommand
        {
            get
            {
                if (_adminButtonCommand == null)
                {
                    _adminButtonCommand = new RelayCommand(param => this.AdminButton(), null);
                }
    
                return _adminButtonCommand;
            }
        }
    
        private void AdminButton()
        {
            MainModel.ContentType = "Admin";
            MainModel.TitleText = "Hello Admin!";
            MainModel.BannedName = "John Doe";
            MainModel.ContentYesButton = "Ban";
            MainModel.ContentNoButton = "AntiBan";
        }
    
        #endregion
    
        public MainViewModel() 
        {
            MainModel = new MainModel();
        }
    }
    

    MainModel.cs

    public class MainModel : NotificationObject
    {
        #region ContentType
    
        private string _contentType = "";
    
        public string ContentType
        {
            get
            {
                return _contentType;
            }
    
            set
            {
                _contentType = value;
                NotifyPropertyChanged("ContentType");
            }
        }
    
        #endregion
    
        #region TitleText
    
        private string _titleText = "";
    
        public string TitleText
        {
            get
            {
                return _titleText;
            }
    
            set
            {
                _titleText = value;
                NotifyPropertyChanged("TitleText");
            }
        }
    
        #endregion
    
        #region BannedName
    
        private string _bannedName = "";
    
        public string BannedName
        {
            get
            {
                return _bannedName;
            }
    
            set
            {
                _bannedName = value;
                NotifyPropertyChanged("BannedName");
            }
        }
    
        #endregion
    
        #region ContentYesButton
    
        private string _contentYesButton = "";
    
        public string ContentYesButton
        {
            get
            {
                return _contentYesButton;
            }
    
            set
            {
                _contentYesButton = value;
                NotifyPropertyChanged("ContentYesButton");
            }
        }
    
        #endregion
    
        #region ContentNoButton
    
        private string _contentNoButton = "";
    
        public string ContentNoButton
        {
            get
            {
                return _contentNoButton;
            }
    
            set
            {
                _contentNoButton = value;
                NotifyPropertyChanged("ContentNoButton");
            }
        }
    
        #endregion
    }
    

    Result for User

    Result for user

    Result for Admin

    Result for admin

    Notes about this way

    Advantages:

    • just one DataTemplate

    Disadvantages:

    • if the representations are very different from each other, in the View the need to manipulate a lot of controls and data.

    Conclusion:

    This method is suitable for Views, which is not very different from each other, and the number of different values is not too large.

    Both examples are available at this link.

    0 讨论(0)
提交回复
热议问题