XamlPad小程序
闲着没事,仿照SDK的样子自己做了一个。
程序主要使用了System.Windows.Markup命名空间下的XamlReader类和XamlWriter,
其中XamlReader可以将Xaml代码转换为元素示例,而XamlWriter类可以将元素示例保存成“流”的形式。(我在之前的文章中介绍过这两个类)
首先仿照SDK中的xamlPad构建出相应的界面,页面主要包括以下几个控件。一个Label控件,用于呈现XAML代码所显示的界面;一个TextBox控件,用于输入xaml代码;还有一个GridSpliter,用于调节2这的布局比例;TextBox下面紧接着一个TextBlock控件,用于显示错误信息。整体代码如下:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="4*"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="0"/>
<GridSplitter Height="2" Grid.Row="1" HorizontalAlignment="Stretch" ></GridSplitter>
<GroupBox Grid.Row="2">
<DockPanel>
<Label DockPanel.Dock="Bottom" VerticalAlignment="Bottom" VerticalContentAlignment="Bottom" Height="30"></Label>
<TextBox Name="xamlTB" Foreground="Red" AcceptsReturn="True" VerticalScrollBarVisibility="Visible" ></TextBox>
</DockPanel>
</GroupBox>
</Grid>
下面看一下具体的逻辑代码:
string xaml = value.ToString().Trim();
XmlReader reader = new XmlTextReader(new StringReader(xaml));
object obj = System.Windows.Markup.XamlReader.Load(reader);
XamlReader.Load方法需要一个流对象,因为XAML是基于XML的,所以很自然的选用了XmlReader对象。这样就通过Load方法得到了一个WPF的元素,将它存储在obj变量中。得到元素后并不能直接将它赋值给Label对象,因为可能会发生错误:
我们可能得到多种类型的元素,如过元素类型是Window,对于Window对象来说,它只能是根级元素,不能让它作为其他元素的子元素;如果是Page对象,它只能成为Window元素或者Frame的子元素;其他元素就好说了,可以直接将他们赋值给Label的Content属性:
if ((obj as Window) != null)
{
#region
if (win != null)
{
win.Close();
win = null;
}
win = obj as Window;
win.Show();
#endregion
}
else if ((obj as Page) != null)
{
#region
StreamWriter sw = new StreamWriter(path);
XamlWriter.Save(obj, sw);
sw.Close();
Frame f = new Frame();
f.Source = new Uri(path, UriKind.RelativeOrAbsolute);
#endregion
}
else
{
///.........
}
整个的程序结构大概就是这样了,还是比较简单的。不过在程序的制作过程中,有一个问题困扰了我很久。究竟是应该的使用原始的.Net事件呢?还是应该使用WPF的绑定呢?实际上我在做项目的时候也遇到了这个问题。那是因为对时间要求很紧,我还是选择了前者(因为更加熟悉: P)。但这个程序我全部使用了WPF的特性,现在就给大家介绍一下:
文本框输入,结果输出到Label中,这可以说是WPF非常典型的绑定“案例”了。不过将XAML直接输出到Label上是没有意义的,需要进行XAML-Element的转化,所以使用了IValueConverter接口。我创建了一个XamlToElement类。该类实现了IValueConverter接口IConvert和ConverterBack方法,并且在Converter中实现了对xaml到元素的转换操作。同时为了让错误信息的及时的显示到TextBlock上,XamlToElement实现了INotifyPropertyChanged接口,用于及时消息通知。完整的代码如下:
XAML:
<Window x:Class="XamlPad.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:XamlPad"
Title="XamlPad" Width="746" Height="540"
>
<Window.Resources>
<local:XamlToElement x:Key="xamlToElement"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="4*"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="0"
Content="{Binding Path=Text,
ElementName=xamlTB,
Converter={StaticResource xamlToElement}}"/>
<GridSplitter Height="2" Grid.Row="1" HorizontalAlignment="Stretch" ></GridSplitter>
<GroupBox Grid.Row="2">
<DockPanel>
<Label DockPanel.Dock="Bottom" VerticalAlignment="Bottom" VerticalContentAlignment="Bottom" Height="30"
Content="{Binding Path=ErrorMessage,Source={StaticResource xamlToElement}}" ></Label>
<TextBox Name="xamlTB" Foreground="Red" AcceptsReturn="True" VerticalScrollBarVisibility="Visible" ></TextBox>
</DockPanel>
</GroupBox>
</Grid>
</Window>
C#:
public class XamlToElement : IValueConverter,INotifyPropertyChanged
{
/// <summary>
/// xaml转换后得到的Window对象
/// </summary>
Window win;
/// <summary>
/// xaml转换后得到的元素,其中包括Page
/// </summary>
object obj;
/// <summary>
/// 判断元素类型,Window=true,Page=false,element=null;
/// </summary>
bool? flag;
/// <summary>
/// Page的存储路径
/// </summary>
string path = Environment.CurrentDirectory + "\\temp.xaml";
private string errorMessage;
public string ErrorMessage
{
get { return errorMessage; }
set
{
errorMessage = value;
OnPropertyChanged("ErrorMessage");
}
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string xaml = value.ToString().Trim();
XmlReader reader = new XmlTextReader(new StringReader(xaml));
try
{
//得到元素对象
obj = System.Windows.Markup.XamlReader.Load(reader);
ErrorMessage = "";
if ((obj as Window) != null)
{
#region
flag = true;
if (win != null)
{
win.Close();
win = null;
}
win = obj as Window;
win.Show();//这里会出现闪屏的情况,大家忍耐以下吧,^_^
return null;
#endregion
}
else if ((obj as Page) != null)
{
#region
flag = false;
StreamWriter sw = new StreamWriter(path);
XamlWriter.Save(obj, sw);
sw.Close();
Frame f = new Frame();
f.Source = new Uri(path, UriKind.RelativeOrAbsolute);
return f;
#endregion
}
else
{
flag = null;
return obj;
}
}
catch (System.Exception a)
{
ErrorMessage = a.Message;
if (flag == true)
return "";
else if (flag == false)
{
Frame f = new Frame();
f.Source = new Uri(path, UriKind.RelativeOrAbsolute);
return f;
}
else
return obj;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
public event PropertyChangedEventHandler PropertyChanged;
// OnPropertyChanged to update property value in binding
private void OnPropertyChanged(string propName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propName));
}
}
}
整个程序就介绍完了,希望大家多提意见:P
源代码下载