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="" 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自定义依赖集合属性无法触发更新的问题的更多相关文章
- WPF的依赖项属性
WPF的依赖项属性 属性与事件是.NET抽象模型的核心部分.WPF使用了更高级的依赖项属性(Dependency Property)功能来替换原来.NET的属性,实现了更高效率的保存机制,还添加了附加 ...
- WPF 自定义依赖属性
原博客地址:http://www.cnblogs.com/DebugLZQ/archive/2012/11/30/2796021.html DependencyObject和Dependen ...
- 【WPF学习笔记】之WPF基础:依赖关系属性和通知
这些天来,对象似乎已经忙得晕头转向了.每个人都希望它们做这做那.Windows® Presentation Foundation (WPF) 应用程序中的典型对象会接到各种各样不同的请求:有要求绑定到 ...
- 迟到的 WPF 学习 —— 依赖项属性
本章学习依赖项属性,英文原文 Dependency Property,它是传统 .Net Framework 属性的扩展,是 WPF 的专属,但所幸使用起来和传统属性几乎一样.WPF 元素所提供的大多 ...
- 学习WPF——初识依赖项属性
入门 首先创建一个依赖项属性 然后绑定父容器的DataContext到这个依赖项的实例 接着绑定子元素的属性到依赖项属性(注意Button的Content属性) 程序最终的运行结果: 说明 首先是 ...
- [转]WPF 依赖项属性
from:http://blog.csdn.net/datoumimi/article/details/8033682 ps:环境限制,发的东西一长就会被拦截,所以删了一部分 UI软件中经常会用到大量 ...
- 浅谈WPF依赖项属性
浅谈WPF依赖项属性 0. 引言 依赖项属性虽然在使用上和CLR属性一样,但是它是WPF特有的,不同于CLR属性.只是封装为我们常用CLR的属性,在语法使用上和CLR属性一样.WPF中一些功能:动画, ...
- 如何在 WPF 中获取所有已经显式赋过值的依赖项属性
原文:如何在 WPF 中获取所有已经显式赋过值的依赖项属性 获取 WPF 的依赖项属性的值时,会依照优先级去各个级别获取.这样,无论你什么时候去获取依赖项属性,都至少是有一个有效值的.有什么方法可以获 ...
- 使用Vue-TreeSelect组件的时候,用watch变量方式解决弹出编辑对话框界面无法触发更新的问题
在前篇随笔<使用Vue-TreeSelect组件实现公司-部门-人员级联下拉列表的处理>中介绍了Vue-TreeSelect组件的使用,包括使用v-modal绑定值,normalizer ...
随机推荐
- mysql source、mysqldump 导入导出数据(转)
解决了mysql gbk编码的导入导出问题,感谢作者. 一.导入数据 1.确定 数据库默认编码,比如编码 为gbk,将读入途径编码同样设为gbk,命令为: set names gb ...
- 如何在Windows下开发Python:在cmd下运行Python脚本+如何使用Python Shell(command line模式和GUI模式)+如何使用Python IDE
http://www.crifan.com/how_to_do_python_development_under_windows_environment/ 本文目的 希望对于,如何在Windows下, ...
- [NOIP2013] 提高组 洛谷P1967 货车运输
题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多 ...
- 享元模式FlyweightPattern(转)
解释一下概念:也就是说在一个系统中如果有多个相同的对象,那么只共享一份就可以了,不必每个都去实例化一个对象.比如说一个文本系统,每个字母定一个对象,那么大小写字母一共就是52个,那么就要定义52个对象 ...
- Scrapy学习-24-集成elasticsearch
elasticsearch简单集成到scrapy中 使用elasticsearch的python接口处理数据 https://github.com/elastic/elasticsearch-dsl ...
- HDU 5997 rausen loves cakes(启发式合并 + 树状数组统计答案)
题目链接 rausen loves cakes 题意 给出一个序列和若干次修改和查询.修改为把序列中所有颜色为$x$的修改为$y$, 查询为询问当前$[x, y]$对应的区间中有多少连续颜色段. ...
- 3)nginx的启动与停止、重启,linux配置对外端口
[启动] 启动代码格式:nginx安装目录地址 -c nginx配置文件地址例如: [root@LinuxServer sbin]# /usr/local/nginx/sbin/nginx -c /u ...
- Delphi 释放数组中的数据
FillChar(aryTest[Low(aryTest)], Length(aryTest) * SizeOf(aryTest[Low(aryTest)]), 0);
- php+mysql两次左外联跨表查询
代码如下: $querySel="select * from roomsy rsy left join room ro on rsy.RoomID=ro.ID left join hotel ...
- Hadoop一些问题总结
1.运行mr程序出错 connecting to resoucemanager retrying .... retrying ..... 原因是没有启动yarn或者启动失败 2.初始化工作目录结构 h ...