XAML与XML类似,就是XML延伸过来的。为了更好的表达一些功能,WPF对XML做了扩展,有些功能是WPF在后台悄悄的替你做了。有时候,虽然实现了某个功能,但是对实现原理还是很茫然。今天就讲讲XAML中赋值操作。

1 通过类型转换赋值

赋值是最简单最常见的操作,举例:

 <Button  Width="" Height="">
</Button>

这里把Width值赋值为200;用代码实现赋值,则为Button.With = 200; 这种赋值操作很直接,大家都能理解。但是仔细想想,感觉有点不对劲。XAML表达式Width="200",这里200是字符串,Width类型是double。字符串200怎么就转换成double了!你会说,200很明显可以转换为double类型,有什么大惊小怪的!

有时,程序实现的逻辑操作很傻瓜,人很容易理解的事,程序并不一定能理解。需要你告诉XAML编译器,怎么把字符串型转换成double型。确实有 一个转换类悄悄的把字符串型转换成了double型。

通过元文件,可以查到Width属性定义。

//
// 摘要:
// 获取或设置元素的宽度。
//
// 返回结果:
// 元素的宽度,单位是与设备无关的单位(每个单位 1/96 英寸)。默认值为 System.Double.NaN。此值必须大于等于 0.0。有关上限信息,请参见“备注”。
[Localizability(LocalizationCategory.None, Readability = Readability.Unreadable)]
[TypeConverter(typeof(LengthConverter))]
public double Width { get; set; }
Width属性定义[TypeConverter(typeof(LengthConverter))]。这句话就表明width转换类型是LengthConverter。当XAML编译器看到Width赋值操作,就会调用LengthConverter。输入是字符串,返回就是double。
你可能感觉到,对这个属性讲解有点啰嗦。我这里是想告诉你:几乎所有的赋值操作,都需要这种转换。
引申: 更深一步讲,如果我们定义了一个属性,这个属性是一个复杂的类型。在XAML如何赋值? 比如自己定义了类型如下:
public class MyPointItem
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}

有一个类包含此属性:

  public class MyClass
{
public MyPointItem Item { get; set; }
}

在XAML语法中如何对Item赋值,XAML语法只认识字符串型。这时需要参考上文Width处理方式。需要自己定义些转换类。定义一个类型继承TypeConverter,实现里面的函数。

比如这样赋值MyClass.Item = "123,456";你需要告诉编译器,如何将"123,456"转化成类型MyPointItem。这里字符串用逗号分隔,你可以用别的符号分隔;比如“#”,只要你的转换函数能处理就行。完整的处理函数如下:

//定义转换类型
public class MyPointItemConverter : TypeConverter
{
public override bool CanConvertFrom( ITypeDescriptorContext context, Type sourceType)
{
if (sourceType is string)
return true;
return base.CanConvertFrom(context, sourceType);
} public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType is MyPointItem)
return true; return base.CanConvertTo(context, destinationType);
} public override object ConvertFrom(ITypeDescriptorContext context,
CultureInfo culture, object value)
{
if (value is string)
{
try
{
return MyPointItem.Parse(value as string);
}
catch (Exception ex)
{
throw new Exception(string.Format("Cannot convert '{0}' ({1}) because {2}", value, value.GetType(), ex.Message), ex);
}
} return base.ConvertFrom(context, culture, value);
} public override object ConvertTo(ITypeDescriptorContext context,
CultureInfo culture, object value, Type destinationType)
{
if (destinationType == null)
throw new ArgumentNullException("destinationType"); MyPointItem gpoint = value as MyPointItem; if (gpoint != null)
if (this.CanConvertTo(context, destinationType))
return gpoint.ToString(); return base.ConvertTo(context, culture, value, destinationType);
}
} //自定义类型
[TypeConverter(typeof(MyPointItemConverter))]
public class MyPointItem
{
public double Latitude { get; set; }
public double Longitude { get; set; } internal static MyPointItem Parse(string data)
{
if (string.IsNullOrEmpty(data))
return new MyPointItem(); string[] items = data.Split(','); //用逗号分隔,和XAML赋值中字符串分隔符保持一致
if (items.Count() != )
throw new FormatException("should have both latitude and longitude"); double lat, lon;
try
{
lat = Convert.ToDouble(items[]);
}
catch (Exception ex)
{
throw new FormatException("Latitude value cannot be converted", ex);
} try
{
lon = Convert.ToDouble(items[]);
}
catch (Exception ex)
{
throw new FormatException("Longitude value cannot be converted", ex);
} return new MyPointItem() { Latitude=lat, Longitude=lon };
}
}

转换类型不是万能的: 只有类型转换,也会遇到难以处理的情况。比如MyClass.Item = "null"。我的意思是将Item赋值为null。但是编译不会这么处理,仍然会调用转换类型MyPointItemConverter,结果就会抛出异常!WPF为此又引入了扩展标识符的概念。

2 扩展标识符

扩展标识符有特殊的语法,如果属性赋值为null,语法如下:
MyClass.Item ="{x:Null}"; 这里的Null其实是一个类型,继承自MarkupExtension;
//
// 摘要:
// 实现 XAML 标记以返回 null 对象,可使用该对象在 XAML 中将值显式设置为 null。
[MarkupExtensionReturnType(typeof(object))]
[TypeForwardedFrom("PresentationFramework, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")]
public class NullExtension : MarkupExtension
{
//
// 摘要:
// 初始化 System.Windows.Markup.NullExtension 类的新实例。
public NullExtension(); //
// 摘要:
// 提供要用作值的 null 作为此标记扩展的输出。
//
// 参数:
// serviceProvider:
// 可为标记扩展实现提供服务的对象。
//
// 返回结果:
// 空引用。
public override object ProvideValue(IServiceProvider serviceProvider);
}
MyClass.Item ="{x:Null}"这句话的意思就是:编译器生成类型NullExtension,调用函数ProvideValue,将此返回值赋值给MyClass.Item;

再举个例子:
Height="{x:Static SystemParameters.IconHeight}”;
编译器处理逻辑是:生成类型StaticExtension,将字符串“SystemParameters.IconHeight”传给构造函数,调用函数ProvideValue,返回double类型。
其实StaticExtension会将字符串“SystemParameters.IconHeight”认为一个静态变量。XAML眼里只有字符串!

绑定 -- 一种很常用的扩展标识符类型
看如下语法:
 <Button  Width="" Height=""
Content="{Binding Height,RelativeSource={RelativeSource Self}}">
</Button>

对content的赋值,是不是感到一头雾水! binding其实也是扩展标识,最终继承自MarkupExtension;

  Binding : BindingBase --> BindingBase : MarkupExtension;

所以binding的作用也是将字符串转换成我们需要的类型。不过binding的参数比较多,有时候需要转好几个弯,才能找到真的源头!

对于上面的赋值,咱做个分析,来看看编译器处理的步骤:

1)生成Binding类型,构造函数传入“Height”,

2)Binding有一个属性为RelativeSource,参见元文件

 //
// 摘要:
// 通过指定绑定源相对于绑定目标的位置,获取或设置绑定源。
//
// 返回结果:
// 一个 System.Windows.Data.RelativeSource 对象,该对象指定要使用的绑定源的相对位置。默认值为 null。
[DefaultValue(null)]
public RelativeSource RelativeSource { get; set; }

仔细看看代码,属性类型和变量名称都是RelativeSource,这是c#语法允许的。当然,这样做会使人困惑!

  RelativeSource={RelativeSource Self},第一个RelativeSource其实是Binding的属性名称,第二个是类型名。Self是一个枚举值。

这句话的意思就是,生成一个类型RelativeSource,构造函数是枚举值Self;将这个变量赋值给属性RelativeSource。

3) 当Content需要值时,就会调用Binding的ProvideValue。这个函数就会把Button的属性Height返回!

当然这里绕了很大一圈,只实现了一个简单的操作:将Button的高度显示出来!感觉好费劲!
但是:绑定有一个特点,可以感知“源”变量的变化!举例如下
 <StackPanel>
<Button x:Name="btnTest" Width="" Height=""
Content="{Binding Height,RelativeSource={RelativeSource Self}}">
</Button>
<Button Margin="" Width="" Height="" Click="Button_Click">增加高度</Button>
</StackPanel>

Button_Click函数:
 private void Button_Click(object sender, RoutedEventArgs e)
{
btnTest.Height += ;
}

当执行Button_Click时,btnTest的高度增加10,显示内容也随之变化。是不是很神奇!为什么会变化?这里需要了解WPF特殊的属性“依赖属性”。这里就不深入讲解了!

当然绑定的优点不仅仅是这些,WPF会用到大量绑定,如果这些绑定都用代码来实现,太繁琐,也不易理解。

总结:微软为了让XAML好用,费了很多心思。为了XAML能做更多的工作,编译器会替你做很多事情!简单的一个赋值操作,背后门道很多!初学者如果不了解这些门道,就感到一片茫然!本文初步揭示了赋值操作背后的一些内幕,希望你读后,有豁然开朗的感觉!

 
												

XAML属性赋值转换之谜(WPF XAML语法解密)的更多相关文章

  1. XAML属性和事件

    1.元素属性 XAML是一种声明性语言,XAML编译器会为每一个标签创建一个与之对应的对象.对象创建出来之后要对它的属性进行必要的初始化之后才有使用意义.因为XAML语言不能写程序运行逻辑,所以一份X ...

  2. 标记扩展和 WPF XAML

      本主题介绍 XAML 的标记扩展概念,包括其语法规则.用途以及底层的类对象模型. 标记扩展是 XAML 语言以及 XAML 服务的 .NET 实现的常规功能. 本主题专门详细论述了用于 WPF X ...

  3. XAML 属性元素,标记扩展和注释

    这节来讲一下XAML中的属性元素,标记扩展,和注释. 属性元素 一般的,我们想要对一个标签的属性赋值,可以直接在标签内部键入属性名给其赋值,如我们给button的Content属性赋值: <Bu ...

  4. WPF XAML

    xmlns 在xml中专门用于声明名字控件, xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 是 ...

  5. 使用MVVM DataTemplate在WPF XAML视图之间切换

    原文 使用MVVM DataTemplate在WPF XAML视图之间切换 更新:这个技术的改进版本,一个不创建视图,可以在以下链接找到: http://www.technical-recipes.c ...

  6. 使用MVVM DataTriggers在WPF XAML视图之间切换/Window窗口自适应内容大小并居中

    原文 使用MVVM DataTriggers在WPF XAML视图之间切换 相关文章: http://www.technical-recipes.com/2016/switching-between- ...

  7. WPF XAML之bing使用StringFormat

    WPF XAML之bing使用StringFormat // 转化为百分比 Text="{Binding Progress, StringFormat=\{0:P\}}" < ...

  8. xml 转换成对象(采用反射机制对对对象属性赋值)

    /// <summary> /// 采用反射机制对对对象属性赋值 /// </summary> /// <param name="node">& ...

  9. [WPF,XAML] 跳动的心

    原文:[WPF,XAML] 跳动的心 没什么艺术细胞,原谅,原谅! <Canvas Width="0" Height="0"> <Canvas ...

随机推荐

  1. XML与DTD

    什么是XML XML个称为Extensible Markup Language,意思是可扩展的标记语言. 应用常见 配置文件 <?xml version="1.0" enco ...

  2. redis简单使用

    主要参考资料:http://wiki.jikexueyuan.com/project/redis-guide/data-type.html一.redis 安装1.在官网下载安装包2.解压安装包 tar ...

  3. 你真的会Xilinx FPGA的复位吗?

    Get Smart About Reset: Think Local, Not Global. 对于复位信号的处理,为了方便我们习惯上采用全局复位,博主在很长一段时间内都是将复位信号作为一个I/O口, ...

  4. tarjan算法的补充POJ2533tarjan求度

    做题时又遇到了疑惑,说明一开始就没有完全理解 基于dfs的tarjan,搜索时会有四种边 树枝边:DFS 时经过的边,即 DFS 搜索树上的边 前向边:与 DFS 方向一致,从某个结点指向其某个子孙的 ...

  5. robotium测试创建java文件和junit文件区别

    两者本身差别不大,只是构造方式不同,我一般都用java文件来创建. 1.当有源码测试时,创建junit测试文件 ActivityInstrumentationTestCase2<T>T可以 ...

  6. Excel中单元格、超级链接形成超级链接单元格

    使用函数 HYPERLINK(超链接,显示文字) =HYPERLINK("http://www.cnblogs.com/Vpygamalion/","李汉超") ...

  7. .net MVC, webAPI,webForm集成steeltoe+springcloud实现调用服务中心服务的总结

    开始之前,如果没接触过Autofac的,可以移步到Autofac官方示例学习一下怎么使用:https://github.com/autofac/Examples .net 下集成steeltoe进行微 ...

  8. strom ui Topology 可视化视图各个指标含义说明

    In the visualization, spout components are represented as blue, while bolts are colored between gree ...

  9. 针对 easyui boolean 布尔值 的处理

    1.html代码 <select class="easyui-combobox" data-options="editable:false,panelHeight: ...

  10. 【转】Windows IIS注册asp 此操作系统版本不支持此选项 错误解决方法

    原文:https://blog.csdn.net/sweety820/article/details/79538973 更新Win10,原来的IIS站点访问不了,原因是因为IIS 没有.net 4.5 ...