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

后台代码如下:

 public partial class SateChart : UserControl
{
static bool isShowBD = true;
static bool isShowGPS = true;
static bool isShowGLONASS = true;
//卫星图标半径
static int r = ;
public static DependencyProperty SateSourceProperty; public IEnumerable<Sate> SateSource
{
get { return (IEnumerable<Sate>)GetValue(SateSourceProperty); }
set { SetValue(SateSourceProperty, value); }
}
static SateChart()
{
SateSourceProperty = DependencyProperty.Register(
"SateSource", typeof(IEnumerable<Sate>), typeof(SateChart), new FrameworkPropertyMetadata(
null, new PropertyChangedCallback(OnSateSourceChanged)));
}
public SateChart()
{
InitializeComponent();
}
private static void OnSateSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != null)
{
SateChart sateChart = (SateChart)sender;
clearSates(sateChart);
drawSates(sateChart, (IEnumerable<Sate>)e.NewValue);
}
} private void bdView_Click(object sender, RoutedEventArgs e)
{
isShowBD = this.bdView.IsChecked == true ? true : false;
} private void gpsView_Click(object sender, RoutedEventArgs e)
{
isShowGPS = this.gpsView.IsChecked == true ? true : false;
} private void glnssView_Click(object sender, RoutedEventArgs e)
{
isShowGLONASS = this.glnssView.IsChecked == true ? true : false;
}
//画卫星图
private static void drawSates(SateChart sateChart, IEnumerable<Sate> staes)
{
// clearSates(); foreach (Sate item in staes)
{
switch (item.SateType)
{
case SateTypes.BD: if (isShowBD) addSate(sateChart, item); break;
case SateTypes.GPS: if (isShowGPS) addSate(sateChart, item); break;
case SateTypes.GLONASS: if (isShowGLONASS) addSate(sateChart, item); break;
default: break;
}
}
}
//在卫星图上添加卫星
private static void addSate(SateChart sateChart, Sate sate)
{
double azimuth = double.Parse(sate.Azimuth);
double elevation = double.Parse(sate.Elevation);
double cosLen = getCosLen(r, elevation);
//卫星图片显示
//Image image = new Image();
//image.Source = new BitmapImage(new Uri("images/satellite2.png", UriKind.Relative));
//image.Width = imageWidth;
//image.Height = imageHeight;
//Canvas.SetTop(image, getY(cosLen, azimuth) - imageHeight/2);
//Canvas.SetLeft(image, getX(cosLen, azimuth) - imageWidth/2);
//myCanvas.Children.Add(image);
Ellipse el = new Ellipse();
el.Width = ;
el.Height = ;
Canvas.SetTop(el, getY(cosLen, azimuth) - 1.5);
Canvas.SetLeft(el, getX(cosLen, azimuth) - 1.5);
SolidColorBrush sb;
switch (sate.SateType)
{
case SateTypes.BD: sb = new SolidColorBrush(Colors.Orange); break;
case SateTypes.GPS: sb = new SolidColorBrush(Colors.Red); break;
case SateTypes.GLONASS: sb = new SolidColorBrush(Colors.Blue); break;
default: sb = new SolidColorBrush(Colors.Orange); break;
}
el.Stroke = sb;
el.Fill = sb; TextBlock tb = new TextBlock();
tb.Text = sate.PRN;
tb.Foreground = new SolidColorBrush(Colors.White);
tb.FontSize = ;
Canvas.SetTop(tb, getY(cosLen, azimuth) + );
Canvas.SetLeft(tb, getX(cosLen, azimuth) + ); sateChart.myCanvas.Children.Add(el);
sateChart.myCanvas.Children.Add(tb);
}
//清空图上的卫星
private static void clearSates(SateChart sateChart)
{
int count = sateChart.myCanvas.Children.Count;
string[] ellipseNames=new string[]{"e1","e2","e3","e4","e5","e6"};
string[] tbNames = new string[] {"tb1","tb2","tb3","tb4","tb5","tb6","tb7","tb8","tb9","tb10","tb11"
,"tb12","tb13","tb14","tb15","tb16","tb17"};
for (int i = count - ; i >= ; i--)
{
Ellipse ep = sateChart.myCanvas.Children[i] as Ellipse;
if (ep != null)
{
if (!ellipseNames.Contains(ep.Name))
sateChart.myCanvas.Children.RemoveAt(i);
}else
{
TextBlock tb = sateChart.myCanvas.Children[i] as TextBlock;
if (tb != null)
{
if(!tbNames.Contains(tb.Name))
sateChart.myCanvas.Children.RemoveAt(i);
}
} }
}
//求此点到圆心的距离
private static double getCosLen(double r, double elevation)
{
double x = ( - elevation / ) * r;
return x;
}
//求X坐标
private static double getX(double cosLen, double azimuth)
{
double x = Math.Sin(azimuth * Math.PI / ) * cosLen;
return x + r;
}
//求Y坐标
private static double getY(double cosLen, double azimuth)
{
double y = Math.Cos(azimuth * Math.PI / ) * cosLen;
return r - y;
}
}

  

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

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

 

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="",CNO=,Elevation="",PRN="" };
Sate sate2 = new Sate() { SateType = SateTypes.BD, Azimuth = "", CNO = , Elevation = "", PRN = "" };
Sate sate3 = new Sate() { SateType = SateTypes.BD, Azimuth = "", CNO = , Elevation = "", PRN = "" };
Sate sate4 = new Sate() { SateType = SateTypes.BD, Azimuth = "", CNO = , Elevation = "", PRN = "" };
Sate sate5 = new Sate() { SateType = SateTypes.BD, Azimuth = "", CNO = , Elevation = "", PRN = "" };
_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));
}

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

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

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

WPF自定义依赖集合属性无法触发更新的问题的更多相关文章

  1. WPF的依赖项属性

    WPF的依赖项属性 属性与事件是.NET抽象模型的核心部分.WPF使用了更高级的依赖项属性(Dependency Property)功能来替换原来.NET的属性,实现了更高效率的保存机制,还添加了附加 ...

  2. WPF 自定义依赖属性

      原博客地址:http://www.cnblogs.com/DebugLZQ/archive/2012/11/30/2796021.html    DependencyObject和Dependen ...

  3. 【WPF学习笔记】之WPF基础:依赖关系属性和通知

    这些天来,对象似乎已经忙得晕头转向了.每个人都希望它们做这做那.Windows® Presentation Foundation (WPF) 应用程序中的典型对象会接到各种各样不同的请求:有要求绑定到 ...

  4. 迟到的 WPF 学习 —— 依赖项属性

    本章学习依赖项属性,英文原文 Dependency Property,它是传统 .Net Framework 属性的扩展,是 WPF 的专属,但所幸使用起来和传统属性几乎一样.WPF 元素所提供的大多 ...

  5. 学习WPF——初识依赖项属性

    入门 首先创建一个依赖项属性 然后绑定父容器的DataContext到这个依赖项的实例 接着绑定子元素的属性到依赖项属性(注意Button的Content属性) 程序最终的运行结果:   说明 首先是 ...

  6. [转]WPF 依赖项属性

    from:http://blog.csdn.net/datoumimi/article/details/8033682 ps:环境限制,发的东西一长就会被拦截,所以删了一部分 UI软件中经常会用到大量 ...

  7. 浅谈WPF依赖项属性

    浅谈WPF依赖项属性 0. 引言 依赖项属性虽然在使用上和CLR属性一样,但是它是WPF特有的,不同于CLR属性.只是封装为我们常用CLR的属性,在语法使用上和CLR属性一样.WPF中一些功能:动画, ...

  8. 如何在 WPF 中获取所有已经显式赋过值的依赖项属性

    原文:如何在 WPF 中获取所有已经显式赋过值的依赖项属性 获取 WPF 的依赖项属性的值时,会依照优先级去各个级别获取.这样,无论你什么时候去获取依赖项属性,都至少是有一个有效值的.有什么方法可以获 ...

  9. 使用Vue-TreeSelect组件的时候,用watch变量方式解决弹出编辑对话框界面无法触发更新的问题

    在前篇随笔<使用Vue-TreeSelect组件实现公司-部门-人员级联下拉列表的处理>中介绍了Vue-TreeSelect组件的使用,包括使用v-modal绑定值,normalizer ...

随机推荐

  1. [canvas入坑3] 类似ps中魔术棒或者画图中油漆桶的功能

    查看效果请到 http://philippica.github.io/  点击fill 这功能其实实现很low,最早高一看黑书的时候看到了floodfill算法感觉好神奇,转念一想这不就是bfs么!! ...

  2. 重绘(repaints)与重排(reflows)

    当页面布局和几何属性改变时就需要"重排" 避免在修改样式的过程中使用 offsetTop, scrollTop, clientTop, getComputedStyle() 这些属性, 它们都会刷新渲 ...

  3. Transformer解析与tensorflow代码解读

    本文是针对谷歌Transformer模型的解读,根据我自己的理解顺序记录的. 另外,针对Kyubyong实现的tensorflow代码进行解读,代码地址https://github.com/Kyuby ...

  4. BZOJ1709超级弹珠

    1709: [Usaco2007 Oct]Super Paintball超级弹珠 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 352  Solved:  ...

  5. .NET结束外部进程 C#结束外部进程

    原文发布时间为:2011-02-15 -- 来源于本人的百度文章 [由搬家工具导入] using System;namespace ConsoleApplication2{    class Prog ...

  6. 5种你未必知道的JS和CSS交互的方法

    随着浏览器不断的升级改进,CSS和JavaScript之间的界限越来越模糊.本来它们是负责着完全不同的功能,但最终,它们都属于网页前端技术,它们需要相互密切的合作.我们的网页中都有.js文件和.css ...

  7. @RequestMapping定义不同的处理器映射规则

    通过@RequestMapping注解可以定义不同的处理器映射规则. 1. URL路径映射 @RequestMapping(value="item")或@RequestMappin ...

  8. sublime text3中成功使用bootstrap3

    在视图这里卡了挺久的,一直是自己在研究.其实自己有一个坏毛病,遇到问题,在网上搜集下找不到便寻求帮助(大多数是求助无效果,因为自己也没搞懂), 这时候自己就会懈怠一会,然后隔一两天心血起伏后便又继续干 ...

  9. OSX 系统无法直接用 Chrome 双击点击打开本地 html 文件

    在 Mac OS X 下,文件经常会被附加上 OS X 特有的扩展属性 ( extend attributes ),具体表现是用 ls -l 查看时会有 @ 的标记,譬如: $ ls -l index ...

  10. centos7.2安装tomcat8

    环境: 阿里云centos7.2 tomcat8.0.32 jdk8.131 1 上传tomcat安装包到服务器的/home(个人习惯) 2 解压安装包 [root@iZt4n6h3u4k407nni ...