问题描述
我正在使用 wpf、.net 和 devexpress 开发应用程序。我主要将 devexpress 用于报表设计器和网格。 我需要在会话中为报表设计器加载自定义字体,但无法在系统中安装它们,因为用户可能没有管理权限。 报表设计器似乎依赖于“InstalledFontCollection”来向用户展示可用字体。
正如 MSDN 文档指出的 here,我使用“AddFontResourceW”后跟“SendMessage”动态加载字体。 但是,随后调用“InstalledFontCollection”不会注册新加载的字体。但是,如果我重新启动应用程序,它会列出它们。 使用 RemoveFontResourceW 后跟 SendMessage 会出现相同的现象。 这是我为测试它而构建的程序的一部分。
1 - 负责字体相关内容的类:
public static class FontManager
{
static public IEnumerable<string> ListInstalledFonts()
{
var installedFonts = new InstalledFontCollection();
var result = from f in installedFonts.Families select f.Name;
installedFonts.Dispose();
return result;
}
[DllImport("gdi32.dll",EntryPoint = "AddFontResourceW",SetLastError = true)]
public static extern int AddFontResource([In][MarshalAs(UnmanagedType.LPWStr)] string lpFileName);
[DllImport("gdi32.dll",EntryPoint = "RemoveFontResourceW",SetLastError = true)]
public static extern int RemoveFontResource([In][MarshalAs(UnmanagedType.LPWStr)] string lpFileName);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd,uint wMsg,IntPtr wParam,IntPtr lParam);
static IntPtr HWND_BROADCAST = new IntPtr(0xffff);
const uint WM_FONTCHANGE = 0x001D;
static public void LoadFont(string path)
{
Console.WriteLine("Loading Font : " + path);
if (AddFontResource(path) > 0)
{
Console.WriteLine(string.Format("Font {0} installed successfully.",path));
SendMessage(HWND_BROADCAST,WM_FONTCHANGE,IntPtr.Zero,IntPtr.Zero);
}
else
{
int error = Marshal.GetLastWin32Error();
if (error != 0)
Console.WriteLine(string.Format("Error Installing font : {0} : {1}",path,new Win32Exception(error).Message));
}
}
static public void UnloadFont(string path)
{
Console.WriteLine("Removing Font : " + path);
if (RemoveFontResource(path) > 0)
{
Console.WriteLine(string.Format("Font {0} removed successfully.",IntPtr.Zero);
}
else
{
int error = Marshal.GetLastWin32Error();
if (error != 0)
Console.WriteLine(string.Format("Error Removing Font : {0} : {1}",new Win32Exception(error).Message));
}
}
}
2 - 向 ui 呈现字体列表的视图模型:
public class MainWindowVM : INotifyPropertyChanged
{
#region ctor
public MainWindowVM()
{
RefreshAvailableFonts();
RefreshVisibleFonts();
CmdRefreshAvailableFonts = new RelayCommand(RefreshAvailableFonts);
CmdRefreshVisibleFonts = new RelayCommand(RefreshVisibleFonts);
CmdLoadCustomFonts = new RelayCommand(LoadCustomFonts);
CmdUnloadCustomFonts = new RelayCommand(UnloadCustomFonts);
}
#endregion
#region propertyNotifier
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(propertyName));
}
#endregion propertyNotifier
// property used to present font list to an ui control,for instance a grid
private IEnumerable<string> visibleFonts = null;
public IEnumerable<string> VisibleFonts
{
get => visibleFonts;
set
{
visibleFonts = value;
NotifyPropertyChanged();
}
}
// property used to cache font list available in system
private IEnumerable<string> availableFonts = null;
public IEnumerable<string> AvailableFonts
{
get => availableFonts;
set
{
availableFonts = value;
NotifyPropertyChanged();
RefreshVisibleFonts();
}
}
// property used with textbox to filter visible fonts in grid
private string filter = null;
public string Filter
{
get => filter;
set
{
filter = value;
NotifyPropertyChanged();
RefreshVisibleFonts();
}
}
#region Commands
// command used to refresh visible font list manually from ui
public ICommand CmdRefreshVisibleFonts { get; private set; }
public void RefreshVisibleFonts()
{
Console.WriteLine("=> RefreshVisibleFonts()");
if (filter == null)
VisibleFonts = from fn in AvailableFonts select fn;
else VisibleFonts = from fn in AvailableFonts where fn.ToLower().Contains(filter.ToLower()) select fn;
}
// command used to refresh available font list manually from ui
public ICommand CmdRefreshAvailableFonts { get; private set; }
public void RefreshAvailableFonts()
{
Console.WriteLine("=> RefreshAvailableFonts()");
AvailableFonts = FontManager.ListInstalledFonts();
}
// command used to load custom fonts manually in the session
public ICommand CmdLoadCustomFonts { get; private set; }
public void LoadCustomFonts()
{
Console.WriteLine("=> LoadCustomFonts()");
var docs = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string fonts = Path.Combine(docs,"fonts");
FontManager.LoadFont(Path.Combine(fonts,@"Pancho-Semibold.ttf"));
FontManager.LoadFont(Path.Combine(fonts,@"Pancho-Medium.ttf"));
FontManager.LoadFont(Path.Combine(fonts,@"Pancho-Regular.ttf"));
}
// command used to unload custom fonts manually from the session
public ICommand CmdUnloadCustomFonts { get; private set; }
public void UnloadCustomFonts()
{
Console.WriteLine("=> UnloadCustomFonts()");
var docs = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string fonts = Path.Combine(docs,"fonts");
FontManager.UnloadFont(Path.Combine(fonts,@"Pancho-Semibold.ttf"));
FontManager.UnloadFont(Path.Combine(fonts,@"Pancho-Medium.ttf"));
FontManager.UnloadFont(Path.Combine(fonts,@"Pancho-Regular.ttf"));
}
#endregion Commands
}
3 - 背后的主窗口代码:我还添加了一个 winproc 钩子来检查在使用 sendMessage 添加或删除字体时消息是否有效广播:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
source.AddHook(WndProc);
}
private IntPtr WndProc(IntPtr hwnd,int msg,IntPtr lParam,ref bool handled)
{
if (msg == 0x1D)
{
Console.WriteLine("Fontchange message received");
if (DataContext is ViewModels.MainWindowVM vm)
vm.RefreshAvailableFonts();
}
return IntPtr.Zero;
}
}
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)