首页 > 代码库 > WPF后台写ControlTemplate总结
WPF后台写ControlTemplate总结
这段时间写ControlTemplate的时候发现绑定的时候有些问题需要总结:
实例ControlTemplate如下:
<UserControl x:Class="ArcGISWpfMarkTest.TestSymbol" 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" xmlns:esri="http://schemas.esri.com/arcgis/client/2009" xmlns:local="clr-namespace:ArcGISWpfMarkTest" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <UserControl.Resources> <esri:MarkerSymbol x:Key="Small" OffsetX="60" OffsetY="72"> <esri:MarkerSymbol.ControlTemplate> <ControlTemplate> <Grid> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal" /> <VisualState x:Name="MouseOver"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="ImageNormal"> <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="ImageSelected"> <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="label0"> <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> <VisualStateGroup x:Name="SelectionStates"> <VisualState x:Name="Unselected" /> <VisualState x:Name="Selected"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="ImageNormal"> <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="ImageSelected"> <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="label0"> <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Grid> <Grid.RowDefinitions> <RowDefinition Height="0.2*"></RowDefinition> <RowDefinition Height="0.2*"></RowDefinition> <RowDefinition Height="0.8*"></RowDefinition> </Grid.RowDefinitions> <Label x:Name="label0" Visibility="Visible" Content="{Binding LabelContent1,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" Grid.Row="0" BorderThickness="0.5" BorderBrush="White" HorizontalContentAlignment="Center" Padding="0" Margin="0" Background="#2b5e93" VerticalContentAlignment="Center" Foreground="White" Width="120" Height="20" FontSize="12"/> <Label x:Name="label1" Visibility="Visible" Content="{Binding LabelContent2,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" Grid.Row="1" BorderThickness="0.5" BorderBrush="White" HorizontalContentAlignment="Center" Padding="0" Margin="0" Background="#2b5e93" VerticalContentAlignment="Center" Foreground="White" Width="120" Height="20" FontSize="12"/> <Image Name="ImageNormal" Grid.Row="2" Source="/Images/small_police.png" Visibility="Visible"/> <Image Name="ImageSelected" Grid.Row="2" Source="/Images/small_police_hit.png" Visibility="Hidden"/> </Grid> </Grid> </ControlTemplate> </esri:MarkerSymbol.ControlTemplate> </esri:MarkerSymbol> </UserControl.Resources> <Grid> </Grid> </UserControl>
这个ControlTemplate是我定义的Arcgis runtime for wpf中MarkerSymbol的ControlTemplate
首先,我遇到第一个问题是:在map窗口的主程序中调用TestSymbol这个类的实例来引用它的ControlTemplate资源时候遇到两个Label的Content无法绑定
情况如下:TestSymbol类中我定义了LabelContent1和LabelContent2两个属性,在Map窗口的类中:
TestSymbol testSymbol=new TestSymbol();
testSymbol.TryFindResources("Small") as Symbol作为一个Graphic点位的Symbol,这样发现ControlTemplate定义的绑定写法其实根本就错误的。
ControlTemplate作为资源是被Map的主窗口调用,这样它沿着可视树查找的时候的DataContext就是Map的主窗口,所以LabelContent1和LabelContent2两个属性应该定义到
Map的主窗口类中,并且绑定应该这样写Content="{Binding LabelContent1,RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
但是这样做的话对我的程序来说没有意义,我不可能每个点位都分别定义LabelContent1和LabelContent2两个属性,所以只能另外想办法了。
我重新定义了一个类用来存每个点位的相关信息
using ESRI.ArcGIS.Client; using ESRI.ArcGIS.Client.Symbols; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Markup; using System.Windows.Media; using System.Windows.Media.Imaging; namespace ArcGISWpfMarkTest { class TestElement:Graphic { private string manName; public string ManName { get { return manName; } set { manName = value; } } private string phoneNo; public string PhoneNo { get { return phoneNo; } set { phoneNo = value; } } public TestElement() { } public TestElement(string ManName,string PhoneNo) { this.ManName = ManName; this.PhoneNo = PhoneNo; ImageNormal = "pack://application:,,,/Source/Images/man32.png"; ImageSelected = "/Source/Images/man_selected32.png"; Symbol = GetMarkerSymbol() as Symbol; } public MarkerSymbol GetMarkerSymbol() { MarkerSymbol markerSymbol = new MarkerSymbol(); markerSymbol.OffsetX = 60; markerSymbol.OffsetY = 72; markerSymbol.ControlTemplate = GetControlTemplate(); return markerSymbol; } public string ImageNormal { get; set; } public string ImageSelected { get; set; } private ControlTemplate GetControlTemplate() { string template = "<ControlTemplate xmlns=‘http://schemas.microsoft.com/winfx/2006/xaml/presentation‘ xmlns:x=‘http://schemas.microsoft.com/winfx/2006/xaml‘><Grid><VisualStateManager.VisualStateGroups><VisualStateGroup x:Name=\"SelectionStates\"><VisualState x:Name=\"Unselected\" /><VisualState x:Name=\"Selected\"><Storyboard><ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=\"(UIElement.Visibility)\" Storyboard.TargetName=\"ImageNormal\"><DiscreteObjectKeyFrame KeyTime=\"0\" Value=http://www.mamicode.com/"{x:Static Visibility.Hidden}\"/></ObjectAnimationUsingKeyFrames><ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=\"(UIElement.Visibility)\" Storyboard.TargetName=\"ImageSelected\"><DiscreteObjectKeyFrame KeyTime=\"0\" Value=http://www.mamicode.com/"{x:Static Visibility.Visible}\"/></ObjectAnimationUsingKeyFrames></Storyboard></VisualState></VisualStateGroup></VisualStateManager.VisualStateGroups><Grid><Grid.RowDefinitions><RowDefinition Height=\"0.2 * \"></RowDefinition><RowDefinition Height=\"0.2 * \"></RowDefinition><RowDefinition Height=\"0.8 * \"></RowDefinition></Grid.RowDefinitions><Label Visibility=\"Visible\" Grid.Row=\"0\" BorderThickness=\"0.5\" BorderBrush=\"White\" HorizontalContentAlignment=\"Center\" Padding=\"0\" Margin=\"0\" Background=\"" + "#2b5e93" + "\" VerticalContentAlignment=\"Center\" Foreground=\"White\" Content=\"" + ManName + "\" Width=\"" + "120" + "\" Height=\"" + "20" + "\" FontSize=\"" + "12" + "\"/><Label Visibility=\"Visible\" Grid.Row=\"1\" BorderThickness=\"0.5\" BorderBrush=\"White\" HorizontalContentAlignment=\"Center\" Padding=\"0\" Margin=\"0\" Background=\"" + "#2b5e93" + "\" VerticalContentAlignment=\"Center\" Foreground=\"White\" Content=\"" + PhoneNo + "\" Width=\"" + "120" + "\" Height=\"" + "20" + "\" FontSize=\"" + "12" + "\"/><Image Name=\"ImageNormal\" Grid.Row=\"2\" Source=\"" + ImageNormal + "\" Visibility=\"Visible\"/><Image Name=\"ImageSelected\" Grid.Row=\"2\" Source=\"" + ImageSelected + "\" Visibility=\"Hidden\"/></Grid></Grid></ControlTemplate>"; var tem = XamlReader.Parse(template); return tem as ControlTemplate; } } }
这个TestElement类的关键思想是把xmal代码定义成字符串格式,然后这个字符串是依赖于TestElement类的自身属性而动态生成的,不同实例传入的属性不同,所以突破了xmal绑定机制的限制,可以成功的为每个点位传入不同的属性值了,这个xmal代码字符串通过以下代码成功转为ControlTemplate了。
var tem = XamlReader.Parse(template); return tem as ControlTemplate;
以上做法有点违背wpf思想设计初衷,我也是没办法才如此做的,谁叫我的WPF技能不够高呢。。
在查找使用C#代码后台定义ControlTemplate的时候,发现这篇博文的方法也可行,只是再写布局的时候有些麻烦,我暂时还没有去实现
http://blog.csdn.net/zyloveyrf/article/details/6736844
还有一个问题总结:
在写Image的资源路径的时候遇到这样一个问题:
在Demo程序中这样定义Image的Resource可行
"pack://application:,,,/Source/Images/man32.png" 绝对路径
"/Source/Images/man_selected32.png" 相对路径
但是有时候上面的写法无效,非要下面的写法才行:
"pack://application:,,,/SDGPS_ManLayer;component/Source/Images/man32.png";
"pack://application:,,,/SDGPS_ManLayer;component/Source/Images/man_selected32.png";
这种情况发生在我的图层是单独编译成一个DLL,与MAP的主程序分离设计的。
我想可能是程序资源集引用的相关问题,以后再做研究。
WPF的Resource路径参考:
http://www.cnblogs.com/kushisei/p/5747708.html
https://msdn.microsoft.com/library/aa970069(v=vs.100).aspx
WPF后台写ControlTemplate总结