首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >WPF MVVM将窗口图标绑定到视图模型中的属性

WPF MVVM将窗口图标绑定到视图模型中的属性
EN

Stack Overflow用户
提问于 2017-12-18 10:52:13
回答 2查看 4.1K关注 0票数 0

我有一个WPF MVVM应用程序。在我的项目属性中,我在“应用程序”选项卡中设置了图标。

我试图统一窗口图标在我的整个应用程序中从不同的地方获得的方式。因此,我在一个类中创建了一个扩展方法:

代码语言:javascript
代码运行次数:0
运行
复制
internal static class IconUtilities
{
    [DllImport("gdi32.dll", SetLastError = true)]
    private static extern bool DeleteObject(IntPtr hObject);

    public static ImageSource ToImageSource(this Icon icon)
    {
        Bitmap bitmap = icon.ToBitmap();
        IntPtr hBitmap = bitmap.GetHbitmap();

        ImageSource wpfBitmap = Imaging.CreateBitmapSourceFromHBitmap(
            hBitmap,
            IntPtr.Zero,
            Int32Rect.Empty,
            BitmapSizeOptions.FromEmptyOptions());

        if (!DeleteObject(hBitmap))
        {
            throw new Win32Exception();
        }

        return wpfBitmap;
    }
}

在我看来,公共财产的模式是:

代码语言:javascript
代码运行次数:0
运行
复制
public Icon WindowIcon
{
    get
    {
        return Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
    }
}

public ImageSource GetWindowIcon()
{
    return WindowIcon.ToImageSource();
}

因此,在我需要在视图模型中获取应用程序图标的地方,我调用这个属性来获得它。所以我确保我总是使用相同的应用程序图标。

现在,我试图通过以下操作将该属性绑定到窗口图标:

代码语言:javascript
代码运行次数:0
运行
复制
<Window x:Name="MainWindow" x:Class="My.Apps.WPF.wMain"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:my="http://schemas.microsoft.com/wpf/2008/toolkit" 
    Icon="{Binding WindowIcon}"
/>
</Window>

但是在运行时,所显示的窗口图标是默认的,而不是在项目属性中设置的。我做错什么了?

尝试#1

这是在InitializeComponents之前调用的,所以我已经进入了加载窗口的事件。

现在,从窗口:

代码语言:javascript
代码运行次数:0
运行
复制
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    this.Icon = ((wMainViewModel)this.DataContext).WindowIcon;
}

我知道错误:

代码语言:javascript
代码运行次数:0
运行
复制
Cannot convert implicitly 'System.Drawing.Icon' type into 'System.Windows.Media.ImageSource'

所以我试着做以下几件事:

代码语言:javascript
代码运行次数:0
运行
复制
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    this.Icon = ((wMainViewModel)this.DataContext).GetWindowIcon();
}

现在我发现了一个错误:

代码语言:javascript
代码运行次数:0
运行
复制
ImageSource for Icon property should be an icon file.

测试应用程序

简单的WPF测试应用程序(非MVVM) -仅用于测试目的

下面我编写了一个完整的WPF应用程序(不是MVVM),它取自这里并添加了一些代码。此示例代码与上面发布的所有代码不同:此示例的目的是在将ImageSource分配给窗口图标时显示问题。它已经在Visual 2008中用.NET Framework3.5和Visual 2015以及.NET Framework3.5进行了测试。在设置SplashScreen代码隐藏构造函数时,这两种情况都会失败,请参阅下面的代码。

用于测试的图标文件(*.ico)也是:"C:\Program \Internet“。此图标文件已添加到我的项目中,我已将build操作设置为资源,而不是将其复制到输出目录作为其属性。

此外,在项目属性中,在应用程序选项卡中,我选择这个图标(bing.ico)作为应用程序的图标。此图标将使用以下行从代码中提取:

System.Drawing.Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);

稍后请参阅我的代码,仅在SplashScreen代码背后,在构造函数中。

附加备注:我有Windows8.1 Pro x64。

App.cs

代码语言:javascript
代码运行次数:0
运行
复制
using System;
using System.Drawing;
using System.Reflection;
using System.Threading;
using System.Windows;

namespace SplashDemo
{
    class App: Application
    {
        [STAThread ( )]
        static void Main ( )
        { 
            Icon ico = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
            Splasher.Splash = new SplashScreen(ico);
            Splasher.ShowSplash();

            for ( int i = 0; i < 5000; i++ )
            {                
                DispatcherHelper.DoEvents();
                Thread.Sleep ( 1 );
            }

            new App ( );
        }

        public App ( )
        {         
            StartupUri = new System.Uri ( "MainWindow.xaml", UriKind.Relative );

            Run ( );            
        }
    }
}

Notes:图标对象类型为System.Drawing.Icon

DispatcherHelper类

代码语言:javascript
代码运行次数:0
运行
复制
using System;
using System.Security.Permissions;
using System.Windows.Threading;

namespace SplashDemo
{
    public static class DispatcherHelper
    {
        /// <summary>
        /// Simulate Application.DoEvents function of <see cref=" System.Windows.Forms.Application"/> class.
        /// </summary>
        [SecurityPermissionAttribute ( SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode )]
        public static void DoEvents ( )
        {
            DispatcherFrame frame = new DispatcherFrame ( );
            Dispatcher.CurrentDispatcher.BeginInvoke ( DispatcherPriority.Background,
                new DispatcherOperationCallback ( ExitFrames ), frame );

            try
            {
                Dispatcher.PushFrame ( frame );
            }
            catch ( InvalidOperationException )
            {
            }
        }

        private static object ExitFrames ( object frame )
        {
            ( ( DispatcherFrame ) frame ).Continue = false;

            return null;
        }
    }
}

Splasher类

代码语言:javascript
代码运行次数:0
运行
复制
using System;
using System.Windows;

namespace SplashDemo
{
    /// <summary>
    /// Helper to show or close given splash window
    /// </summary>
    public static class Splasher
    {
        /// <summary>
        /// 
        /// </summary>
        private static Window mSplash;

        /// <summary>
        /// Get or set the splash screen window
        /// </summary>
        public static Window Splash
        {
            get
            {
                return mSplash;
            }
            set
            {
                mSplash = value;
            }
        }

        /// <summary>
        /// Show splash screen
        /// </summary>
        public static void ShowSplash ( )
        {
            if ( mSplash != null )
            {
                mSplash.Show ( );
            }
        }
        /// <summary>
        /// Close splash screen
        /// </summary>
        public static void CloseSplash ( )
        {
            if ( mSplash != null )
            {
                mSplash.Close ( );

                if ( mSplash is IDisposable )
                    ( mSplash as IDisposable ).Dispose ( );
            }
        }
    }
}

SplashScreen类(代码隐藏)

代码语言:javascript
代码运行次数:0
运行
复制
using System.Drawing;
using System.Reflection;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace SplashDemo
{
    /// <summary>
    /// Interaction logic for SplashScreen.xaml
    /// </summary>
    public partial class SplashScreen : Window
    {
        public SplashScreen ()
        {
            InitializeComponent();
        }

        public SplashScreen(Icon icon) : this()
        { 
            // BELOW LINE CRASHES
            this.Icon =  System.Drawing.Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
        }
    }
}

关于的说明

  • 如果使用System.Drawing.Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);= this.Icon,它会在编译时抛出以下错误:

不能隐式地将“system.drawing.icon”转换为“system.windows.media.imagesource”

  • 如果我将这一行替换为: this.Icon = icon.ToImageSource2();

然后抛出一个运行时错误(不是编译错误),一个System.InvalidOperationException:

图标属性的ImageSource必须是图标文件

SplashScreen View

代码语言:javascript
代码运行次数:0
运行
复制
<Window 
    x:Class="SplashDemo.SplashScreen"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:SplashDemo" 

    Title="SplashScreen" Height="236" Width="414" WindowStartupLocation="CenterScreen" WindowStyle="None" 
    Background="Orange" BorderBrush="DarkOrange" BorderThickness="3" 
    ShowInTaskbar="False" ResizeMode="NoResize">

    <Grid>
        <Grid.RowDefinitions>            
            <RowDefinition Height="2*" />
            <RowDefinition Height="0.5*" />
        </Grid.RowDefinitions>

        <Label Grid.Row="0" Name="label1" FontSize="48" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Foreground="MintCream">
            <Label.BitmapEffect>
                <OuterGlowBitmapEffect GlowSize="15" />
            </Label.BitmapEffect> Splash screen
        </Label>

        <Label Grid.Row="1" x:Name="CurrentTask" 
               FontFamily="Microsoft Sans Serif" FontSize="18" FontStyle="Italic"  
               Foreground="White" Content="Loading">
            <Label.Style>
                <Style TargetType="Label">
                    <Style.Triggers>
                        <EventTrigger RoutedEvent="Label.Loaded">
                            <EventTrigger.Actions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Content" Duration="00:00:00.8" RepeatBehavior="Forever">
                                            <DiscreteObjectKeyFrame KeyTime="00:00:00.0" Value="Loading"/>
                                            <DiscreteObjectKeyFrame KeyTime="00:00:00.2" Value="Loading."/>
                                            <DiscreteObjectKeyFrame KeyTime="00:00:00.4" Value="Loading.."/>
                                            <DiscreteObjectKeyFrame KeyTime="00:00:00.6" Value="Loading..."/>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </BeginStoryboard>
                            </EventTrigger.Actions>
                        </EventTrigger>
                    </Style.Triggers>
                </Style>
            </Label.Style>
        </Label>

    </Grid>
</Window>

MainWindow View

代码语言:javascript
代码运行次数:0
运行
复制
<Window 
    x:Class="SplashDemo.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Main window" Height="332" Width="539" WindowStartupLocation="CenterScreen">
    <Grid>
        <Label Margin="39,59,21,102" Name="label1" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="36">Application window</Label>
    </Grid>
</Window>

MainWindow代码-幕后

代码语言:javascript
代码运行次数:0
运行
复制
using System.Windows;

namespace SplashDemo
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow ( )
        {
            InitializeComponent ( );
        }
    }
}

IconUtilities类(从System.Drawing.Icon转换为System.Drawing.Icon:

代码语言:javascript
代码运行次数:0
运行
复制
using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace SplashDemo
{
    internal static class IconUtilities
    {
        [DllImport("gdi32.dll", SetLastError = true)]
        private static extern bool DeleteObject(IntPtr hObject);

        public static ImageSource ToImageSource(this Icon icon)
        {
            Bitmap bitmap = icon.ToBitmap();
            IntPtr hBitmap = bitmap.GetHbitmap();

            ImageSource wpfBitmap = Imaging.CreateBitmapSourceFromHBitmap(
                hBitmap,
                IntPtr.Zero,
                Int32Rect.Empty,
                BitmapSizeOptions.FromEmptyOptions());

            if (!DeleteObject(hBitmap))
            {
                throw new Win32Exception();
            }

            return wpfBitmap;
        }

        // Below a simple method without extra objects:
        public static ImageSource ToImageSource2(this Icon icon)
        {
            ImageSource imageSource = Imaging.CreateBitmapSourceFromHIcon(
                                          icon.Handle,
                                          Int32Rect.Empty,
                                          BitmapSizeOptions.FromEmptyOptions());

            return imageSource;
        }
    }
}

这里的注释:

  • ImageSource是System.Windows.Media.ImageSource型的
  • ToImageSource2和ToImageSource一样,但是没有额外的对象,请参见这里
EN

回答 2

Stack Overflow用户

发布于 2017-12-18 10:59:45

绑定

代码语言:javascript
代码运行次数:0
运行
复制
<Window ... Icon="{Binding WindowIcon}">

要求WindowIcon属性是ImageSource

因此,您当然希望实现如下所示的视图模型,它在加载的事件处理程序中没有任何代码支持。

代码语言:javascript
代码运行次数:0
运行
复制
public ImageSource WindowIcon
{
    get { return GetWindowIcon().ToImageSource(); }
}

public static Icon GetWindowIcon()
{
    return Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
}
票数 0
EN

Stack Overflow用户

发布于 2021-11-15 22:09:16

您不能使用ImageSource进行绑定,请查看正式文件。您可以看到,Window.Icon属性接受BitmapFrame作为其输入。

下面是官方文档中提供的代码,描述了如何用代码设置图标。

代码语言:javascript
代码运行次数:0
运行
复制
// Set an icon using code
Uri iconUri = new Uri("pack://application:,,,/WPFIcon2.ico", UriKind.RelativeOrAbsolute);
this.Icon = BitmapFrame.Create(iconUri);

在尝试了所有其他数据类型之后,我可以猜测它只接受BitmapFrame?在微软的代码中,它也使用BitmapFrame.Create(iconUri);,我遇到了这个问题,它表明调试器将Window图标识别为System.Windows.Media.Imaging.BitmapFrameDecode,而不是ImageSource。因此,忘记转换为ImageSourceSystem.Drawing.Icon,只需使用BitmapFrame。只需将ImageSource部件更改为下面所示,就可以解决这个问题。

在MainWindowViewModel.cs中

代码语言:javascript
代码运行次数:0
运行
复制
private BitmapFrame _bfWindowIcon;

public BitmapFrame BfWindowIcon
{
    get { 
        return _bfWindowIcon;
    }
    set {
        _bfWindowIcon = value;
        OnPropertyChanged(nameof(BfWindowIcon));
    }
}

private void VoidUpdateIcon() {
   IconBitmapDecoder ibd = new IconBitmapDecoder(
   new Uri(@"pack://application:,,,/Resources/Icon_Green.ico", UriKind.RelativeOrAbsolute),
   BitmapCreateOptions.None,
   BitmapCacheOption.Default);
   BfWindowIcon = ibd.Frames[0];
}

在MainWindow.xaml中

代码语言:javascript
代码运行次数:0
运行
复制
<Window x:Class=
...
...
Icon="{Binding BfWindowIcon}">
    <Window.DataContext>
        <viewmodels:MainWindowViewModel />
    </Window.DataContext>
...
...
</Window>

在使用Window.Icon时绑定IValueConverter需要一些技巧,因为.xaml中的StaticResource已经过时了。下面是我如何在IValueConverter中使用这个

Converter.cs

代码语言:javascript
代码运行次数:0
运行
复制
public class ServiceStateToWindowIconConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            ServiceState val = (ServiceState)value;
            if (val == ServiceState.OK)
            { 
                return BfGetBitmapFrameByURI("pack://application:,,,/ProjectName;component/Resources/Icon_Green.ico");
            }
            else if (val == ServiceState.Warning)
            {
                return BfGetBitmapFrameByURI("pack://application:,,,/ProjectName;component/Resources/Icon_Orange.ico");
            }
            else
            {
                return BfGetBitmapFrameByURI ("pack://application:,,,/ProjectName;component/Resources/Icon_Red.ico");
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        private BitmapFrame BfGetBitmapFrameByURI(string strUri)
        {
            IconBitmapDecoder ibd = new IconBitmapDecoder(
            new Uri(strUri, UriKind.RelativeOrAbsolute),
            BitmapCreateOptions.None,
            BitmapCacheOption.Default);
            return ibd.Frames[0];
        }

MainWindow.xaml

代码语言:javascript
代码运行次数:0
运行
复制
<Window 
xmlns:Converters="clr-namespace:YourNameSpace"
...
... 
Background="Transparent">
...
    <Window.Resources>
        <Converters:ServiceStateToWindowIconConverter x:Key="ServiceStateToWindowIconConverter"/>
    </Window.Resources>
    <Window.Icon>
        <Binding Path="ServiceStateForConverter" Converter="{StaticResource ServiceStateToWindowIconConverter}"/>
    </Window.Icon>
...
</Window>
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47866904

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档