WPF后台写ControlTemplate总结

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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=\\"{x:Static Visibility.Hidden}\\"/></ObjectAnimationUsingKeyFrames><ObjectAnimationUsingKeyFrames Storyboard.TargetProperty=\\"(UIElement.Visibility)\\" Storyboard.TargetName=\\"ImageSelected\\"><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 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总结的主要内容,如果未能解决你的问题,请参考以下文章

[WPF自定义控件库] 自定义控件的代码如何与ControlTemplate交互

WPF中ControlTemplate和DataTemplate的区别

WPF -- DataTemplate与ControlTemplate结合使用

WPF ControlTemplate简介

WPF ControlTemplate

WPF的ControlTemplate和DataTemplate简介