场景:一个信息窗体,用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不久,不足的地方,还望高手们指正~~