WPF 自定义控件操作自定义控件

  场景:一个信息窗体,用Grid分成两列,左边的是信息列表自定义控件,右边的是信息内容自定义控件(高度未占满整列),当点击信息列表中的某一条信息时,信息内容控件的位置要与列表中所选中的那条信息对齐。

  以下为DEMO代码:

  MainWindows.xaml:     

<Window x:Class="NotifyProperty.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:Msg="clr-namespace:NotifyProperty"
        x:Name="mainWin" 
        Title="MainWindow" Height="600" Width="800">
    <Grid x:Name="grid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        
        <!--信息列表-->
        <Msg:MessageList x:Name="MsgList" Grid.Column="0"/>
        
        <!--信息内容-->
        <Msg:MsgInfo x:Name="msgInfo" Grid.Column="1" Width="300" Height="200" Margin="20,20,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>

        <TextBlock Text="{Binding ElementName=mainWin, Path=MarginTop}" Margin="20,20,0,0" Grid.ColumnSpan="2" Grid.Column="1" />
    </Grid>
</Window>

MainWindows.xaml.cs:   

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;

namespace NotifyProperty
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public Double MarginTop
        {
            get { return (Double)GetValue(MarginTopProperty); }
            set { SetValue(MarginTopProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MarginTop.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MarginTopProperty =
            DependencyProperty.Register("MarginTop", typeof(Double), typeof(MainWindow));

        DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(MarginTopProperty, typeof(MainWindow));
        

        public MainWindow()
        {
            InitializeComponent();

            //属性绑定
            Binding binding = new Binding();
            binding.Source = MsgList;
            binding.Path = new PropertyPath("MarginTop");
            BindingOperations.SetBinding(this, MarginTopProperty, binding);

            //附加属性监听
            dpd.AddValueChanged(this, OnMyDependencyPropertyChanged); 
        }


        /// <summary>
        /// 附加属性监听事件
        /// 计算MsgInfo对象的Margin-Top
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnMyDependencyPropertyChanged(object sender, EventArgs e)
        {
            double mLeft = 20;
            double mTop = 0;

            double parentHeight = grid.ActualHeight-20;

            if (MarginTop + msgInfo.Height > parentHeight)
            {
                mTop = parentHeight - msgInfo.Height;
            }
            else
            {
                mTop = MarginTop;

                if (mTop < 20)
                {
                    mTop = 20;
                }
            }

            msgInfo.Margin = new Thickness(mLeft, mTop, 0, 0);
            
        }
    }
}

  MessageList.xaml:  

<UserControl x:Class="NotifyProperty.MessageList"
             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">
    <Grid x:Name="grid">
        <ListBox x:Name="lvStudent" ItemsSource="{Binding}" Width="{Binding ElementName=grid, Path=ActualWidth}" MouseLeftButtonUp="lvStudent_MouseLeftButtonUp" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Border BorderBrush="Blue" BorderThickness="1" Margin="5" Width="300" Height="80">
                        <TextBlock Text="{Binding Age}" FontSize="26"/>
                    </Border>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</UserControl>

  MessageList.xaml.cs

  

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Diagnostics;

namespace NotifyProperty
{
    /// <summary>
    /// MessageList.xaml 的交互逻辑
    /// </summary>
    public partial class MessageList : UserControl, INotifyPropertyChanged
    {
        private double marginTop;

        public double MarginTop
        {
            get { return marginTop; }
            set 
            { 
                marginTop = value;
                RaisePropertyChanged("MarginTop");
            }
        }

        public MessageList()
        {
            InitializeComponent();

            List<Student> stuList = new List<Student>();

            for (int i = 0; i < 10; i++)
            {
                Student stu = new Student();
                stu.Age = i;
                stuList.Add(stu);
            }

            this.lvStudent.ItemsSource = stuList;
        }

        /// <summary>
        /// 列表点击事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lvStudent_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            ListBoxItem lbi = null;
            if (lvStudent.Items.Count > 0 && lvStudent.SelectedIndex != -1)
            {
                lbi = lvStudent.ItemContainerGenerator.ContainerFromIndex(lvStudent.SelectedIndex) as ListBoxItem;
            }
            
            Point point = e.MouseDevice.GetPosition(this);

            if (lbi != null)
            {
                MarginTop = point.Y - e.MouseDevice.GetPosition(lbi).Y;// +lbi.ActualHeight / 2;
            }
            else
            {
                MarginTop = point.Y;
            }
        }     

        #region Notify Method
        public event PropertyChangedEventHandler PropertyChanged;
        protected void RaisePropertyChanged(string propertyName)
        {
            this.VerifyPropertyName(propertyName);

            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        }

        #region 属性名验证
        protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
        [Conditional("DEBUG")]
        [DebuggerStepThrough]
        public void VerifyPropertyName(string propertyName)
        {
            // If you raise PropertyChanged and do not specify a property name,
            // all properties on the object are considered to be changed by the binding system.
            if (String.IsNullOrEmpty(propertyName))
                return;

            // Verify that the property name matches a real,  
            // public, instance property on this object.
            if (TypeDescriptor.GetProperties(this)[propertyName] == null)
            {
                string msg = "Invalid property name: " + propertyName;

                if (this.ThrowOnInvalidPropertyName)
                    throw new ArgumentException(msg);
                else
                    Debug.Fail(msg);
            }
        }
        #endregion

        #endregion

    }

    public class Student
    {
        public int Age { get; set; }
    }
}

  MsgInfo.xaml:  

<UserControl x:Class="NotifyProperty.MsgInfo"
             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="600" d:DesignWidth="800">
    <Border BorderBrush="Red" BorderThickness="2"/>
</UserControl>

  MsgInfo.xaml.cs: ~Nothing~~

  以上为简单实现的代码,接触WPF不久,不足的地方,还望高手们指正~~

原文地址:https://www.cnblogs.com/Dincat/p/2581735.html