WPF user control, access dependency properties of component elements

前端 未结 2 436
南笙
南笙 2021-01-22 17:14

Problem

A user WPF control is made up of multiple standard controls.

How can multiple dependency properties of the component (base or standard) controls be acc

相关标签:
2条回答
  • 2021-01-22 17:47

    How about the next solution: 1. Create the AttachedProperty (because you must an entry point) and bind this property to the collection of data.This collection of data will contain changes you want perform on sub-controls of a main user control used inside the window. This collection will be defined inside the main window view model. 2. In attached property changed callback get the binded collection, parse it data into sub-controls properties. Here is the solution: 3. Xaml code:

    <Window.DataContext>
        <nirHelpingOvalButton:MainWindowViewModel />
    </Window.DataContext>
    
    <Grid>
        <nirHelpingOvalButton:InnerControl x:Name="MyInnerControl" 
                                           nirHelpingOvalButton:Helper.InnerControlPropertiesAccessor="{Binding InnerData, Mode=Default, UpdateSourceTrigger=PropertyChanged}"/>
    </Grid>
    

    4. Attached property code (bindig support):

    public static readonly DependencyProperty InnerControlPropertiesAccessorProperty = DependencyProperty.RegisterAttached(
            "InnerControlPropertiesAccessor", typeof (ObservableCollection<TargetControlData>), typeof (Helper), new PropertyMetadata(default(ObservableCollection<TargetControlData>), InnerValueAccessProviderPropertyChangedCallback));
    
        public static void SetInnerControlPropertiesAccessor(DependencyObject element, ObservableCollection<TargetControlData> value)
        {
            element.SetValue(InnerControlPropertiesAccessorProperty, value);
        }
    
        public static ObservableCollection<TargetControlData> GetInnerControlPropertiesAccessor(DependencyObject element)
        {
            return (ObservableCollection<TargetControlData>) element.GetValue(InnerControlPropertiesAccessorProperty);
        }
        private static void InnerValueAccessProviderPropertyChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            var control = sender as Control;
            if (control == null) return;
            var valuesMap = args.NewValue as ObservableCollection<TargetControlData>;
            if (valuesMap == null)
                return;
            valuesMap.ToList().ForEach(data => TryToBind(control, data));
        }
    
        private static void TryToBind(Control control, TargetControlData data)
        {
            var innerControl = control.FindName(data.SubControlName) as DependencyObject;
            if (innerControl == null) return;
    
            var myBinding = new Binding
            {
                Source = data,
                Path = new PropertyPath("Data"),
                Mode = BindingMode.TwoWay,
                UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
            };
            var descriptors = TypeDescriptor.GetProperties(innerControl);
            var propertyDescriptor = descriptors.Find(data.SubConrolProperty, true);
            var descriptor = DependencyPropertyDescriptor.FromProperty(propertyDescriptor);
            if (descriptor == null) return;
            var dependencyProperty = descriptor.DependencyProperty;
            BindingOperations.SetBinding(innerControl, dependencyProperty, myBinding);
        }
    

    5. Inner control xaml:

    <UserControl x:Class="NirHelpingOvalButton.InnerControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UniformGrid>
        <Button x:Name="InnerControlButton"></Button>
        <TextBlock x:Name="InnerContentTextBlock"></TextBlock>
    </UniformGrid>
    

    6. ViewModel code:

        public class MainWindowViewModel:BaseObservableObject
    {
        private static int _staticCount = 0;
        private List<Brush> _list = new List<Brush> {Brushes.Green, Brushes.Red, Brushes.Blue};
    
        public MainWindowViewModel()
        {
            InnerData = new ObservableCollection<TargetControlData>
            {
                new TargetControlData
                {
                    SubControlName = "InnerControlButton",
                    SubConrolProperty = "Content",
                    Data = "Click Me",
                },
                new TargetControlData
                {
                    SubControlName = "InnerControlButton",
                    SubConrolProperty = "Command",
                    Data = new RelayCommand(CommandMethod),
                },
                new TargetControlData
                {
                    SubConrolProperty = "Text",
                    SubControlName = "InnerContentTextBlock",
                    Data = "Hello"
                },
                new TargetControlData
                {
                    SubConrolProperty = "Background",
                    SubControlName = "InnerContentTextBlock",
                    Data = Brushes.Green
                },
                new TargetControlData
                {
                    SubConrolProperty = "Foreground",
                    SubControlName = "InnerContentTextBlock",
                    Data = Brushes.White
                },
            };
        }
    
        private void CommandMethod()
        {
            _staticCount ++;
            var backgroundData = InnerData.FirstOrDefault(data => data.SubControlName == "InnerContentTextBlock" && data.SubConrolProperty == "Background");
            var textData = InnerData.FirstOrDefault(data => data.SubControlName == "InnerContentTextBlock" && data.SubConrolProperty == "Text");
            if (backgroundData == null || textData == null) return;
            var index = _staticCount%_list.Count;
            backgroundData.Data  = _list[index];
            textData.Data = string.Format("{0} {1}", "Hello", backgroundData.Data);
        }
        public ObservableCollection<TargetControlData> InnerData { get; set; }}
    

    7. TargetControlData code:

        public class TargetControlData:BaseObservableObject
    {
        private string _subControlName;
        private string _subConrolProperty;
        private object _data;
    
        public string SubControlName
        {
            get { return _subControlName; }
            set
            {
                _subControlName = value;
                OnPropertyChanged();
            }
        }
    
        public string SubConrolProperty
        {
            get { return _subConrolProperty; }
            set
            {
                _subConrolProperty = value;
                OnPropertyChanged();
            }
        }
    
        public object Data
        {
            get { return _data; }
            set
            {
                _data = value;
                OnPropertyChanged();
            }
        }
    }
    
    1. Summary - you can pull control properties data from configuration file, or collect them by reflection.

    regards,

    0 讨论(0)
  • 2021-01-22 17:53

    The way you suggested - I don't think this would be possible. But it can be done with normal properties, instead of dependency properties, something like:

    UserControl xaml:

    <StackPanel>
        <TextBlock x:Name="tbOne"></TextBlock>
        <TextBlock x:Name="tbTwo" Foreground="Red"></TextBlock>
    </StackPanel>
    

    UserControl code behind:

        public string One
        {
            get
            {
                return this.tbOne.Text;
            }
            set
            {
                this.tbOne.Text = value;
            }
        }
    
        public string Two
        {
            get
            {
                return this.tbTwo.Text;
            }
            set
            {
                this.tbTwo.Text = value;
            }
        }
    

    and the usage of user control:

        <local:UserControl1 One="test1" Two="test2"></local:UserControl1>
    
    0 讨论(0)
提交回复
热议问题