WPF中,一个文件选择控件的例子

对我来说,WPF比MFC好了不止一百倍:同样的工作,用C#比用C++,开发效率至少提高了200%;在UI制作方面,WPF比MFC更容易做出绚丽的效果;WPF应用程序能嵌入在IE浏览器中,方便部署、维护。随着Windows 7发布脚步的渐进,未来桌面程序的开发,必定属于WPF的。现在,我们还有理由拒绝WPF吗?

接下是一个WPF中文件选择控件的例子,先上效果图:

image

点击后,出现选择文件对话框:

image

选择文件后,自动将文件显示在图片框中:

image

 

代码上很简单,除去VS自动生成的代码,总代码不超过10行,且都是在设计器里用鼠标完成的,整个过程除了输入一些名称,不需要敲一下键盘,历时2分钟:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Image Viewer" Height="420" Width="480"
    xmlns:n="clr-namespace:Newinfosoft.Windows.Controls;assembly=Newinfosoft.Windows.Controls">
    <Grid>
        <n:FilePicker HorizontalAlignment="Left" Margin="12,12,0,0" Width="Auto" Height="Auto"
        VerticalAlignment="Top" FullPath="Open an image file" x:Name="fs" />
        <Border Margin="12,57,12,12" BorderThickness="1"
            BorderBrush="Black" CornerRadius="1,1,1,1">
            <Image Margin="0,0,0,0" Stretch="Uniform"
            Source="{Binding ElementName=fs,Path=FullPath}" />
        </Border>
    </Grid>
</Window>

如果用MFC?估计光按钮上的圆角矩形就需要花半小时吧?

上面代码的核心就是n:FilePicker这个控件了,接下来,我会用一个Step By Step的方式,实现该控件。

新建一个WPF的Custom Control

image 

在WPF中,Custom Control和User Control一个最大的不同之处在于,前者需要开发人员编写控件模板,而后者不需要。Custom Control的控件模板包含在项目Themes\Generic.xaml中,点击按钮Add后,Visual Studio向导已为我们自动生成该文件:

image

设计模板

我们先编译一下,然后在Expression Blend 打开工程,选择编辑Generic.xaml,在边上的Resources面板里,展开Generic.xaml节点,然后双击FilePicker:

image

可以看到,现在我们的模板仅仅是一个大黑框,其他的什么都没有:

image

 

现在可以编辑模板了,首先选中Style:

image

然后在上面选择Edit Template:  image

 

添加一个StackPanel、一个Image、一个TextBlock,使模板看起来像这个样子:

image

把TextBlock命名为tb:

image

对应的Generix.xaml文件如下:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1">

    <Style TargetType="{x:Type local:FilePicker}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:FilePicker}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <StackPanel Width="Auto" Height="Auto" Orientation="Horizontal">
                            <Image Width="32" Height="32" Margin="4,2,4,2"/>
                            <TextBlock x:Name="tb" Width="Auto" Height="Auto" Text="TextBlock"
                            TextWrapping="Wrap" HorizontalAlignment="Center"
                            VerticalAlignment="Center" Margin="0,0,4,0"/>
                        </StackPanel>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

除了在Generic.xaml里设计模板,我们还需要在静态的构造函数里添加以下代码:

static FilePicker()
{
    DefaultStyleKeyProperty.OverrideMetadata(typeof(FilePicker),
        new FrameworkPropertyMetadata(typeof(FilePicker)));
}

添加属性

回到Visual Studio,添加一个辅助类,来表明这个控件选择的文件路径,以及这个文件的图标:

public class FilePickerInfo : INotifyPropertyChanged
{
    public FilePickerInfo()
    {
        Icon = IconTools.GetPixel3232Icon("__Folder");
    }

    public FilePickerInfo(String fullpath)
    {
        FullPath = fullpath;
    }

    private String m_FullPath;
    public String FullPath
    {
        get
        {
            return m_FullPath;
        }
        set
        {
            if (value != m_FullPath)
            {
                m_FullPath = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("FullPath"));
                }

                FileInfo fi = new FileInfo(FullPath);
                Icon = IconTools.GetPixel3232Icon(fi.Extension);
            }
        }
    }

    private Icon m_Icon;
    public Icon Icon
    {
        get
        {
            return m_Icon;
        }
        set
        {
            if (value != m_Icon)
            {
                m_Icon = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("Icon"));
                }
            }
        }
    }

    #region INotifyPropertyChanged 成员

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion
}

将FilePickerInfo绑定到模板里

利用WPF的绑定,我要将FilePickerInfo的FullPath属性绑定到模板TextBlock的Text属性上,而把Icon绑定到Image控件的Source属性上。

首先需要在控件中添加两个属性,FSInfo和FullPath。之所以使用依赖属性(DependencyProperty ),是因为DependencyProperty 可以animation、styling、binding等……

public FilePickerInfo FSInfo
{
    get { return (FilePickerInfo)GetValue(FSInfoProperty); }
    set { SetValue(FSInfoProperty, value); }
}

// Using a DependencyProperty as the backing store for FSInfo.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty FSInfoProperty =
    DependencyProperty.Register("FSInfo", typeof(FilePickerInfo),
    typeof(FilePicker), new UIPropertyMetadata(null));

public String FullPath
{
    get { return (String)GetValue(FullPathProperty); }
    set { SetValue(FullPathProperty, value); }
}

// Using a DependencyProperty as the backing store for FullPath.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty FullPathProperty =
    DependencyProperty.Register("FullPath", typeof(String), typeof(FilePicker),
    new UIPropertyMetadata("请选择文件", new PropertyChangedCallback(OnFullPathChanged)));

 

 

 

 

上述代码最后一行的OnFullPathChanged方法是回调函数,用来执行当FullPath改变时的操作,具体实现见“添加事件”。

编译后,回到Expression Blend,选择模板里的StackPanel,将FSInfo绑定到DataContext上(使用Template Binding):

image

 

绑定TextBlock的Text属性:

image

 

绑定Image的Source属性,由于Image控件的Source是ImageSource类型,而我们的图标是System.Drawing.Icon类型的,所以在绑定的过程中需要使用类型转换器:

image 

使用的类型转换器代码,将Icon转换成BitmapSource类型:

[ValueConversion(typeof(Icon), typeof(BitmapSource))]
public class ImageConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter,
        System.Globalization.CultureInfo culture)
    {
        if (value == null) return null;

        return Imaging.CreateBitmapSourceFromHIcon((value as Icon).Handle, Int32Rect.Empty,
            BitmapSizeOptions.FromEmptyOptions());
    }
    public object ConvertBack(object value, Type targetType, object parameter,
        System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

添加事件

添加一个事件:

/// <summary>
/// Identifies the ValueChanged routed event.
/// </summary>
public static readonly RoutedEvent FullPathChangedEvent = EventManager.RegisterRoutedEvent(
    "SelectChanged", RoutingStrategy.Bubble,
    typeof(RoutedPropertyChangedEventHandler<String>), typeof(FilePicker));

/// <summary>
/// Occurs when the Value property changes.
/// </summary>
public event RoutedPropertyChangedEventHandler<String> FullPathChanged
{
    add { AddHandler(FullPathChangedEvent, value); }
    remove { RemoveHandler(FullPathChangedEvent, value); }
}

 

当控件的FullPath改变时,抛出该事件:

protected static void OnFullPathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    FilePicker picker = d as FilePicker;

    String oldFullPath = e.OldValue as String;
    String fullPath = e.NewValue as String;

    picker.FSInfo.FullPath = fullPath;

    RoutedPropertyChangedEventArgs<String> ev = new RoutedPropertyChangedEventArgs<String>(
        oldFullPath, fullPath, FullPathChangedEvent);

    picker.RaiseEvent(ev);

}

覆盖OnApplyTemplate函数

修改OnApplyTemplate函数,为模板中的TextBlock添加鼠标单击事件:

public override void OnApplyTemplate()
{
    base.OnApplyTemplate();
    TextBlock tb = this.Template.FindName("tb", this) as TextBlock;
    tb.MouseLeftButtonUp += (sender, e) =>
    {
        FileDialog fd;

        fd = new OpenFileDialog();

        if (fd.ShowDialog() == true)
        {
            FullPath = fd.FileName;
        }

    };
}

编译后,该控件就能使用了,只需要把Image控件的Source和该控件的FullPath绑定在一起:

image

image

 

下载源代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值