zoukankan      html  css  js  c++  java
  • WPF自定义依赖集合属性无法触发更新的问题

      通常WPF中通过继承UserControl的来快速创建自定义控件,最近项目上需要设计一个卫星星图显示控件,最终效果如下图所示。完成过程中遇到了自定义集合依赖属性无法触发更新通知的问题,在此记录一下,方便有相同问题的朋友们可以快速解决,也希望有人能发现更好的解决办法。

      为了完成目的,我写了下面一个SateChart自定义控件类,

    XAML代码如下:

    <UserControl x:Class="WpfApplication1.SateChart"
                 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" >
        <DockPanel Margin="5" LastChildFill="True">
            <StackPanel Margin="1" DockPanel.Dock="Top" Orientation="Horizontal" HorizontalAlignment="Center">
                <Rectangle Fill="Orange" Width="10" Height="10"/>
                <CheckBox Name="bdView" Content="BD-2" Margin="5,0,5,0"  IsChecked="True" Click="bdView_Click"/>
                <Rectangle Fill="Red" Width="10" Height="10"/>
                <CheckBox Name="gpsView" Content="GPS" Margin="5,0,5,0"    IsChecked="True" Click="gpsView_Click"/>
                <Rectangle Fill="Blue" Width="10" Height="10"/>
                <CheckBox Name="glnssView" Content="GLNS" Margin="5,0,5,0"   IsChecked="True" Click="glnssView_Click"/>
            </StackPanel>
            <Viewbox DockPanel.Dock="Bottom" MaxHeight="300" MaxWidth="300">
                <Canvas Name="myCanvas" Width="90" Height="90" >
                    <Ellipse Name="e1" Width="90" Height="90" Fill="Black" Stroke="Black"
                                     HorizontalAlignment="Center"/>
                    <Ellipse Name="e2" Width="90" Height="90" Fill="Black" Stroke="White"
                                     HorizontalAlignment="Center">
                        <Ellipse.RenderTransform>
                            <ScaleTransform ScaleX="0.8333" ScaleY="0.8333" CenterX="45" CenterY="45"/>
                        </Ellipse.RenderTransform>
                    </Ellipse>
                    <Ellipse Name="e3" Width="90" Height="90" Fill="Black" Stroke="White"
                                     HorizontalAlignment="Center">
                        <Ellipse.RenderTransform>
                            <ScaleTransform ScaleX="0.6666" ScaleY="0.6666" CenterX="45" CenterY="45"/>
                        </Ellipse.RenderTransform>
                    </Ellipse>
                    <Ellipse Name="e4" Width="90" Height="90" Fill="Black" Stroke="White"
                                     HorizontalAlignment="Center">
                        <Ellipse.RenderTransform>
                            <ScaleTransform ScaleX="0.5" ScaleY="0.5" CenterX="45" CenterY="45"/>
                        </Ellipse.RenderTransform>
                    </Ellipse>
                    <Ellipse Name="e5" Width="90" Height="90" Fill="Black" Stroke="White"
                                     HorizontalAlignment="Center">
                        <Ellipse.RenderTransform>
                            <ScaleTransform ScaleX="0.333" ScaleY="0.333" CenterX="45" CenterY="45"/>
                        </Ellipse.RenderTransform>
                    </Ellipse>
                    <Ellipse Name="e6" Width="90" Height="90" Fill="Black" Stroke="White"
                                     HorizontalAlignment="Center">
                        <Ellipse.RenderTransform>
                            <ScaleTransform ScaleX="0.1666" ScaleY="0.1666" CenterX="45" CenterY="45"/>
                        </Ellipse.RenderTransform>
                    </Ellipse>
                    <Line Name="line0"  Stroke="White" StrokeDashArray="1 2" StrokeThickness="0.3" X1="45" Y1="90" X2="45" Y2="0" />
                    <Line Name="line1"  Stroke="White" StrokeDashArray="1 2" StrokeThickness="0.3" X1="45" Y1="90" X2="45" Y2="0" >
                        <Line.RenderTransform>
                            <RotateTransform Angle="30" CenterX="45" CenterY="45"/>
                        </Line.RenderTransform>
                    </Line>
                    <Line Name="line2"  Stroke="White" StrokeDashArray="1 2" StrokeThickness="0.3" X1="45" Y1="90" X2="45" Y2="0" >
                        <Line.RenderTransform>
                            <RotateTransform Angle="60" CenterX="45" CenterY="45"/>
                        </Line.RenderTransform>
                    </Line>
                    <Line Name="line3"  Stroke="White" StrokeDashArray="1 2" StrokeThickness="0.3" X1="45" Y1="90" X2="45" Y2="0"  >
                        <Line.RenderTransform>
                            <RotateTransform Angle="90" CenterX="45" CenterY="45"/>
                        </Line.RenderTransform>
                    </Line>
                    <Line Name="line4"  Stroke="White" StrokeDashArray="1 2" StrokeThickness="0.3" X1="45" Y1="90" X2="45" Y2="0" >
                        <Line.RenderTransform>
                            <RotateTransform Angle="120" CenterX="45" CenterY="45"/>
                        </Line.RenderTransform>
                    </Line>
                    <Line Name="line5"  Stroke="White" StrokeDashArray="1 2" StrokeThickness="0.3" X1="45" Y1="90" X2="45" Y2="0" >
                        <Line.RenderTransform>
                            <RotateTransform Angle="150" CenterX="45" CenterY="45"/>
                        </Line.RenderTransform>
                    </Line>
                    <TextBlock Name="tb1" Text="360°" Canvas.Left="43"  Canvas.Top="0" Foreground="White" FontSize="2"/>
                    <TextBlock Name="tb2" Text="30°" Canvas.Left="66" Canvas.Top="7" Foreground="White" FontSize="2" />
                    <TextBlock Name="tb3" Text="60°" Canvas.Left="80" Canvas.Top="22" Foreground="White" FontSize="2" />
                    <TextBlock Name="tb4" Text="90°" Canvas.Left="86" Canvas.Top="45" Foreground="White" FontSize="2" />
                    <TextBlock Name="tb5" Text="120°" Canvas.Left="80" Canvas.Top="64" Foreground="White" FontSize="2" />
                    <TextBlock Name="tb6" Text="150°" Canvas.Left="65" Canvas.Top="80" Foreground="White" FontSize="2" />
                    <TextBlock Name="tb7" Text="180°" Canvas.Left="43" Canvas.Top="86" Foreground="White" FontSize="2" />
                    <TextBlock Name="tb8" Text="210°" Canvas.Left="25" Canvas.Top="82" Foreground="White" FontSize="2" />
                    <TextBlock Name="tb9" Text="240°" Canvas.Left="8" Canvas.Top="67" Foreground="White" FontSize="2" />
                    <TextBlock Name="tb10" Text="270°" Canvas.Left="1" Canvas.Top="45" Foreground="White" FontSize="2" />
                    <TextBlock Name="tb11" Text="300°" Canvas.Left="8" Canvas.Top="21" Foreground="White" FontSize="2" />
                    <TextBlock Name="tb12" Text="330°" Canvas.Left="25" Canvas.Top="6" Foreground="White" FontSize="2" />
                    <TextBlock Name="tb13" Text="15°" Canvas.Left="52" Canvas.Top="6" Foreground="White" FontSize="2" />
                    <TextBlock Name="tb14" Text="30°" Canvas.Left="50" Canvas.Top="14" Foreground="White" FontSize="2" />
                    <TextBlock Name="tb15" Text="45°" Canvas.Left="48" Canvas.Top="21" Foreground="White" FontSize="2" />
                    <TextBlock Name="tb16" Text="60°" Canvas.Left="47" Canvas.Top="29" Foreground="White" FontSize="2" />
                    <TextBlock Name="tb17" Text="75°" Canvas.Left="46" Canvas.Top="35" Foreground="White" FontSize="2" />
                </Canvas>
            </Viewbox>
        </DockPanel>
    </UserControl>
    View Code

    后台代码如下:

      1 public partial class SateChart : UserControl
      2     {
      3         static bool isShowBD = true;
      4         static bool isShowGPS = true;
      5         static bool isShowGLONASS = true;
      6         //卫星图标半径
      7         static int r = 45;
      8         public static DependencyProperty SateSourceProperty;
      9 
     10         public IEnumerable<Sate> SateSource
     11         {
     12             get { return (IEnumerable<Sate>)GetValue(SateSourceProperty); }
     13             set { SetValue(SateSourceProperty, value); }
     14         }
     15         static SateChart()
     16         {
     17             SateSourceProperty = DependencyProperty.Register(
     18                 "SateSource", typeof(IEnumerable<Sate>), typeof(SateChart), new FrameworkPropertyMetadata(
     19                     null, new PropertyChangedCallback(OnSateSourceChanged)));
     20         }
     21         public SateChart()
     22         {
     23             InitializeComponent();
     24         }
     25         private static void OnSateSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
     26         {
     27             if (e.NewValue != null)
     28             {
     29                 SateChart sateChart = (SateChart)sender;
     30                 clearSates(sateChart);
     31                 drawSates(sateChart, (IEnumerable<Sate>)e.NewValue);
     32             }
     33         }
     34 
     35         private void bdView_Click(object sender, RoutedEventArgs e)
     36         {
     37             isShowBD = this.bdView.IsChecked == true ? true : false;
     38         }
     39 
     40         private void gpsView_Click(object sender, RoutedEventArgs e)
     41         {
     42             isShowGPS = this.gpsView.IsChecked == true ? true : false;
     43         }
     44 
     45         private void glnssView_Click(object sender, RoutedEventArgs e)
     46         {
     47             isShowGLONASS = this.glnssView.IsChecked == true ? true : false;
     48         }
     49         //画卫星图
     50         private static void drawSates(SateChart sateChart, IEnumerable<Sate> staes)
     51         {
     52             // clearSates();
     53 
     54             foreach (Sate item in staes)
     55             {
     56                 switch (item.SateType)
     57                 {
     58                     case SateTypes.BD: if (isShowBD) addSate(sateChart, item); break;
     59                     case SateTypes.GPS: if (isShowGPS) addSate(sateChart, item); break;
     60                     case SateTypes.GLONASS: if (isShowGLONASS) addSate(sateChart, item); break;
     61                     default: break;
     62                 }
     63             }
     64         }
     65         //在卫星图上添加卫星
     66         private static void addSate(SateChart sateChart, Sate sate)
     67         {
     68             double azimuth = double.Parse(sate.Azimuth);
     69             double elevation = double.Parse(sate.Elevation);
     70             double cosLen = getCosLen(r, elevation);
     71             //卫星图片显示
     72             //Image image = new Image();
     73             //image.Source = new BitmapImage(new Uri("images/satellite2.png", UriKind.Relative));
     74             //image.Width = imageWidth;
     75             //image.Height = imageHeight;
     76             //Canvas.SetTop(image, getY(cosLen, azimuth) - imageHeight/2);
     77             //Canvas.SetLeft(image, getX(cosLen, azimuth) - imageWidth/2);
     78             //myCanvas.Children.Add(image);
     79             Ellipse el = new Ellipse();
     80             el.Width = 3;
     81             el.Height = 3;
     82             Canvas.SetTop(el, getY(cosLen, azimuth) - 1.5);
     83             Canvas.SetLeft(el, getX(cosLen, azimuth) - 1.5);
     84             SolidColorBrush sb;
     85             switch (sate.SateType)
     86             {
     87                 case SateTypes.BD: sb = new SolidColorBrush(Colors.Orange); break;
     88                 case SateTypes.GPS: sb = new SolidColorBrush(Colors.Red); break;
     89                 case SateTypes.GLONASS: sb = new SolidColorBrush(Colors.Blue); break;
     90                 default: sb = new SolidColorBrush(Colors.Orange); break;
     91             }
     92             el.Stroke = sb;
     93             el.Fill = sb;
     94 
     95             TextBlock tb = new TextBlock();
     96             tb.Text = sate.PRN;
     97             tb.Foreground = new SolidColorBrush(Colors.White);
     98             tb.FontSize = 2;
     99             Canvas.SetTop(tb, getY(cosLen, azimuth) + 1);
    100             Canvas.SetLeft(tb, getX(cosLen, azimuth) + 1);
    101 
    102             sateChart.myCanvas.Children.Add(el);
    103             sateChart.myCanvas.Children.Add(tb);
    104         }
    105         //清空图上的卫星
    106         private static void clearSates(SateChart sateChart)
    107         {
    108             int count = sateChart.myCanvas.Children.Count;
    109             string[] ellipseNames=new string[]{"e1","e2","e3","e4","e5","e6"};
    110             string[] tbNames = new string[] {"tb1","tb2","tb3","tb4","tb5","tb6","tb7","tb8","tb9","tb10","tb11"
    111             ,"tb12","tb13","tb14","tb15","tb16","tb17"};
    112             for (int i = count - 1; i >= 0; i--)
    113             {
    114                 Ellipse ep = sateChart.myCanvas.Children[i] as Ellipse;
    115                 if (ep != null)
    116                 {
    117                     if (!ellipseNames.Contains(ep.Name))
    118                         sateChart.myCanvas.Children.RemoveAt(i);
    119                 }else
    120                 {
    121                     TextBlock tb = sateChart.myCanvas.Children[i] as TextBlock;
    122                     if (tb != null)
    123                     { 
    124                         if(!tbNames.Contains(tb.Name))
    125                             sateChart.myCanvas.Children.RemoveAt(i);
    126                     }
    127                 }
    128 
    129             }
    130         }
    131         //求此点到圆心的距离
    132         private static double getCosLen(double r, double elevation)
    133         {
    134             double x = (1 - elevation / 90) * r;
    135             return x;
    136         }
    137         //求X坐标
    138         private static double getX(double cosLen, double azimuth)
    139         {
    140             double x = Math.Sin(azimuth * Math.PI / 180) * cosLen;
    141             return x + r;
    142         }
    143         //求Y坐标
    144         private static double getY(double cosLen, double azimuth)
    145         {
    146             double y = Math.Cos(azimuth * Math.PI / 180) * cosLen;
    147             return r - y;
    148         }
    149     }
    View Code

      

    然后就在XAML中绑定数据源,后台写好模拟数据源,想着一切ok了

    <local:SateChart x:Name="sateChart" SateSource="{Binding Path=Sates,Mode=OneWay}" />
    View Code

     

    List<Sate> _sates = new List<Sate>();
            public List<Sate> Sates
            {
                get
                {
                    if (_sates == null)
                        _sates = new List<Sate>();
                    return _sates;
                }
                set { _sates = value;
                }
            }
    public MainWindow()
            {
                InitializeComponent();
                this.DataContext = this;
            }
    
            private void Window_Loaded(object sender, RoutedEventArgs e)
            {
                _sates.Clear();
             //   _sates = new List<Sate>();
                Sate sate1 = new Sate() {SateType=SateTypes.BD,Azimuth="50",CNO=21,Elevation="60",PRN="166" };
                Sate sate2 = new Sate() { SateType = SateTypes.BD, Azimuth = "60", CNO = 21, Elevation = "60", PRN = "169" };
                Sate sate3 = new Sate() { SateType = SateTypes.BD, Azimuth = "70", CNO = 21, Elevation = "70", PRN = "180" };
                Sate sate4 = new Sate() { SateType = SateTypes.BD, Azimuth = "80", CNO = 21, Elevation = "80", PRN = "111" };
                Sate sate5 = new Sate() { SateType = SateTypes.BD, Azimuth = "90", CNO = 21, Elevation = "90", PRN = "123" };
                _sates.Add(sate1);
                _sates.Add(sate2);
                _sates.Add(sate3);
                _sates.Add(sate4);
                _sates.Add(sate5);
                OnPropertyChanged("Sates");
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
            private void OnPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this,new PropertyChangedEventArgs(propertyName));
            }
    View Code

     经过调试发现执行OnPropertyChanged("Sates")的时候,却没有触发自定义控件中的自定义依赖属性的OnSateSourceChanged(..)方法,这是为什么呢?自己一番百度没有解决,后来还是把问题发布到最近新加的一个Blend设计群里面,热心的群友果然有经验很多,有人提示说要使用ObservaleCollection,可是我早已试过此方法,并不能解决问题,因为绑定的是我们自定义依赖集合属性,不是ItemsControl的ItemsSource属性。还有人直接道出了有效解决办法,要触发自定义依赖集合属性的更改通知需要先new一下集合,然后再OnPropertyChanged方法,但是为什么会出现这样的问题至今他也没有找到相关资料,只是知道这样能够成功解决问题。

      我本来一开始是考虑继承ItemsControl的,可是没有找到相关的资料,网上大多基本都是继承UserControl的。。。,微软的ItemsControl的ItemsSource属性就可以触发更新通知,我想肯定是有原因的,希望后面有时间好好研究一下。

      问题得以解决,虽然不是最佳的解决办法。下面附上完整的测试用例代码,有兴趣的朋友们可以试验一下我说的情况。有高人知道更好的解决办法,希望不吝赐教。

  • 相关阅读:
    Silverlight项目笔记1:UI控件与布局、MVVM、数据绑定、await/async、Linq查询、WCF RIA Services、序列化、委托与事件
    斯坦福iOS7公开课1-3笔记及纸牌Demo
    使用CocoaPods管理第三方开源类库
    Mac下安装Django
    iOS与H5界面JSBridge交互Demo
    手动导入swift三方danielgindi/Charts到OC工程中教程
    button自适应宽度 并根据屏幕宽自动换行排列
    ReactNative中iOS和Android的style分开设置教程
    react-native DatePicker日期选择组件的实现
    react-native Simulator com+r不能刷新模拟器
  • 原文地址:https://www.cnblogs.com/cangyue080180/p/5670649.html
Copyright © 2011-2022 走看看