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 ...
随机推荐
- cf 853 B Jury Meeting [前缀和]
题面: 传送门 思路: 看完题目以后,首先有一个结论:每个人都是先去到首都,等待开会,开会结束以后再一个个走掉 而且这道题只有去首都和离开首都的机场 因此考虑计算去首都的飞机的前缀最小花费,以及离开首 ...
- 关于jena-fuseki SPARQL query版本问题的解决方案
在做“Apache jena SPARQL endpoint及推理”时,遇到了不少问题,主要原因是jena-fuseki版本更新了.以下对问题解决方案做下笔记.想了解更多,请查阅底部参考文献. Que ...
- BZOJ2123 [Sdoi2013]森林 【主席树 + 启发式合并】
题目 输入格式 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负 ...
- (转载--修改)使用Xcode9的Instruments检测解决iOS内存泄露
作为一名iOS开发攻城狮,在苹果没有出ARC(自动内存管理机制)时,我们几乎有一半的开发时间都耗费在这么管理内存上.后来苹果很人性的出了ARC,虽然在很大程度上,帮助我们开发者节省了精力和时间.但是我 ...
- idea如何设置类头注释和方法注释(带注释模板)
1.idea类注释 打开:file->setting->Editor->Filr and Code Templates->Includes->File Header 类注 ...
- LOJ#2132. 「NOI2015」荷马史诗
$n \leq 100000$个数字,放进$k$叉树里,一个点只能放一个数,使所有数字乘以各自深度这个值之和最小的同时,最大深度的数字最小. 哈夫曼.这是我刚学OI那段时间看到的,感觉就是个很无聊的贪 ...
- mvc3 学习链接收集
原文发布时间为:2011-04-17 -- 来源于本人的百度文章 [由搬家工具导入] The mvc3 study links collection http://dotnetslackers.com ...
- Android蓝牙介绍
1. 介绍 自从Android 4.2开始,Android开始使用自己的蓝牙协议栈BlueDroid,而不是bluez BlueDroid可分为两层: - BTE: Bluetooth Embedde ...
- 《Linux命令行与shell脚本编程大全 第3版》Linux命令行---40
以下为阅读<Linux命令行与shell脚本编程大全 第3版>的读书笔记,为了方便记录,特地与书的内容保持同步,特意做成一节一次随笔,特记录如下:
- ext2/3/4的inode结构说明
系统环境:Ubuntu15.10/ext4 今天在复习<鸟哥的私房菜-基础学习篇>,看到inode大小为128bytes,想看下这128字节里面到底是什么样的. 于是我查了下google, ...