我有一个WPF MVVM应用程序。在我的项目属性中,我在“应用程序”选项卡中设置了图标。
我试图统一窗口图标在我的整个应用程序中从不同的地方获得的方式。因此,我在一个类中创建了一个扩展方法:
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;
}
}
在我看来,公共财产的模式是:
public Icon WindowIcon
{
get
{
return Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
}
}
public ImageSource GetWindowIcon()
{
return WindowIcon.ToImageSource();
}
因此,在我需要在视图模型中获取应用程序图标的地方,我调用这个属性来获得它。所以我确保我总是使用相同的应用程序图标。
现在,我试图通过以下操作将该属性绑定到窗口图标:
<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之前调用的,所以我已经进入了加载窗口的事件。
现在,从窗口:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.Icon = ((wMainViewModel)this.DataContext).WindowIcon;
}
我知道错误:
Cannot convert implicitly 'System.Drawing.Icon' type into 'System.Windows.Media.ImageSource'
所以我试着做以下几件事:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.Icon = ((wMainViewModel)this.DataContext).GetWindowIcon();
}
现在我发现了一个错误:
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:
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类:
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类:
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类(代码隐藏):
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”转换为“system.windows.media.imagesource”
然后抛出一个运行时错误(不是编译错误),一个System.InvalidOperationException:
图标属性的ImageSource必须是图标文件
SplashScreen View:
<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:
<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代码-幕后:
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:
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;
}
}
}
这里的注释:
发布于 2017-12-18 10:59:45
绑定
<Window ... Icon="{Binding WindowIcon}">
要求WindowIcon
属性是ImageSource
因此,您当然希望实现如下所示的视图模型,它在加载的事件处理程序中没有任何代码支持。
public ImageSource WindowIcon
{
get { return GetWindowIcon().ToImageSource(); }
}
public static Icon GetWindowIcon()
{
return Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
}
发布于 2021-11-15 22:09:16
您不能使用ImageSource
进行绑定,请查看正式文件。您可以看到,Window.Icon
属性接受BitmapFrame
作为其输入。
下面是官方文档中提供的代码,描述了如何用代码设置图标。
// 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
。因此,忘记转换为ImageSource
或System.Drawing.Icon
,只需使用BitmapFrame
。只需将ImageSource
部件更改为下面所示,就可以解决这个问题。
在MainWindowViewModel.cs中
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中
<Window x:Class=
...
...
Icon="{Binding BfWindowIcon}">
<Window.DataContext>
<viewmodels:MainWindowViewModel />
</Window.DataContext>
...
...
</Window>
在使用Window.Icon
时绑定IValueConverter
需要一些技巧,因为.xaml中的StaticResource已经过时了。下面是我如何在IValueConverter
中使用这个
Converter.cs
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
<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>
https://stackoverflow.com/questions/47866904
复制相似问题