Excel/VSTO:仅在直接绑定到互操作时出现错误 0x80028018。-提供 属性
Excel/VSTO: error 0x80028018 only when binding directly to interop.-provided property
当我尝试使用此测试应用程序设置工作表的名称 属性 时,由于出现 0x80028018 错误,它无法正常工作。
当我更改第一个文本框中的值并单击选项卡按钮时,没有任何反应,而我希望 Sheet1 被重命名。在 Visual Studio 输出 Window 中,我可以看到以下错误:
System.Windows.Data Error: 8 : Cannot save value from target back to source.
BindingExpression:Path=Worksheet.Name; DataItem='ViewModel' (HashCode=35209123); target element is 'TextBox' (Name=''); target property is 'Text' (type 'String')
ExternalException:'System.Runtime.InteropServices.ExternalException (0x80028018): Exception of type 'System.Runtime.InteropServices.ExternalException' was thrown.
at System.Windows.Forms.ComponentModel.Com2Interop.Com2PropertyDescriptor.SetValue(Object component, Object value)
at MS.Internal.Data.PropertyPathWorker.SetValue(Object item, Object value)
at MS.Internal.Data.ClrBindingWorker.UpdateValue(Object value)
at System.Windows.Data.BindingExpression.UpdateSource(Object value)'
该应用程序是一个非常基本的 WPF 应用程序,其 ViewModel class 包含对工作簿的引用。然后我们有一个绑定到 ViewModel 的视图 (xaml) class。
视图模型 class:
public class ViewModel
{
public Worksheet Worksheet { get; set; }
public ViewModel(Worksheet worksheet)
{
Worksheet = worksheet;
}
public string Name
{
get => Worksheet.Name;
set => Worksheet.Name = value;
}
}
然后,在视图中,我有一个绑定到工作表名称 属性 的 TextBox 控件。
基本上有两种方法可以做到这一点:
<TextBox Text="{Binding Worksheet.Name}"/>
或:
<TextBox Text="{Binding Name}"/>
那是因为我的 ViewModel 同时公开了工作表和名称。
这就是事情变得有趣的地方。
如果我使用第一种方法,它不起作用并且在 visual studio 的调试输出中我发现上面显示的错误。
如果我使用第二种方式,它工作得很好。这种方式是通过 Name 属性,这反过来意味着我有一个明确的
Worksheet.Name = ...
在我的代码中(在名字的 setter 中)。
这是我在两种解决方案之间看到的唯一区别。
进一步分析和问题
0x80028018 似乎是一个众所周知的;有几篇文章在谈论它。
我读了这个:
HowTo: Fix “Old format or invalid type library” error (0x80028018)
但是:
- 我无法解释为什么在我的应用程序中我看到两种不同的行为
- 我不知道如何解决应用程序级别的问题,以便绑定按预期工作
即使问题不是那么严重并且有一个简单的解决方法,也存在它可能指示更大问题的风险。这就是为什么我正在寻找一个强大的解决方案。
遗留代码,如果你想重现它
除了 ViewModel class 之外,还有一个非常简单的 xaml 视图,(仅注意:作为 UserControl 实现,因为它必须加载到 ElementHost 中)。
有一个超文本框只是为了让第一个失去焦点并触发更新。
<UserControl x:Class="ExcelAddIn12.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ExcelAddIn12"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel>
<!--the non-working solution: -->
<TextBox Text="{Binding Worksheet.Name}"/>
<TextBox/>
</StackPanel>
</UserControl>
它只剩下启动和接线代码。作为启动器,我使用了带按钮的功能区(设计的,而不是 xaml)。
public partial class Ribbon1
{
private void Ribbon1_Load(object sender, RibbonUIEventArgs e) { }
private void button1_Click(object sender, RibbonControlEventArgs e)
{
Worksheet myWorksheet = (Worksheet)Globals.ThisAddIn.Application.ActiveSheet;
ViewModel vm = new ViewModel(myWorksheet);
UserControl1 view = new UserControl1() { DataContext = vm };
Form form = new Form();
ElementHost wpfHost = new ElementHost() { Dock = DockStyle.Fill, Child = view };
form.Controls.Add(wpfHost);
form.Show();
}
}
版本:
- Visual Studio 2017(英文)
- Excel 2010(英文)
您链接的文章说明如下:
Most of the Excel Object Model methods and properties require specifying an LCID (locale identifier). If a client computer has the English version of Excel, and the locale for the current user is configured for another language (e.g. German or French), Excel may fire the “Old format or invalid type library” exception with error code 0x80028018 (-2147647512).
它还告诉您将 System.Threading.Thread.CurrentThread.CultureInfo
设置为 Excel 使用的那个。最简单的方法是对当前 (UI) 线程全局执行一次:
Thread.CurrentThread.CurrentCulture = new CultureInfo(Application.LanguageSettings.LanguageID[MsoAppLanguageID.msoLanguageIDUI])
至于发生这种情况的原因,看起来在通过绑定直接调用和从包装器调用时使用了不同的 LCID 属性。在进行这些调用时,您可能需要检查 Thread.CurrentThread.CurrentCulture
和 Thread.CurrentThread.CurrentUICulture
属性。
这里有几个链接,可提供有关不同区域性设置的更多背景信息:
- Setting Culture (en-IN) globally in WPF application
- #763 – The Difference Between CurrentCulture and CurrentUICulture
- Application Culture / UICulture
- WPF Bindings and CurrentCulture Formatting
简而言之,CurrentThread.CurrentCulture
是您当前的区域设置(您可以通过控制面板更改),CurrentThread.CurrentUICulture
对应于您安装的 Windows 语言(您通常不能轻易更改),CultureInfo.DefaultThreadCurrentCulture
将更改当前的 CurrentThread.CurrentCulture
属性 并为未来的线程设置默认值,最后有类似于以下行的内容适用于 WPF 绑定:
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(
CultureInfo.CurrentCulture.IetfLanguageTag)));
当然,您也可以将本地用户的文化更改为 Excel 正在使用的文化(英语)。
当我尝试使用此测试应用程序设置工作表的名称 属性 时,由于出现 0x80028018 错误,它无法正常工作。
当我更改第一个文本框中的值并单击选项卡按钮时,没有任何反应,而我希望 Sheet1 被重命名。在 Visual Studio 输出 Window 中,我可以看到以下错误:
System.Windows.Data Error: 8 : Cannot save value from target back to source.
BindingExpression:Path=Worksheet.Name; DataItem='ViewModel' (HashCode=35209123); target element is 'TextBox' (Name=''); target property is 'Text' (type 'String')
ExternalException:'System.Runtime.InteropServices.ExternalException (0x80028018): Exception of type 'System.Runtime.InteropServices.ExternalException' was thrown.
at System.Windows.Forms.ComponentModel.Com2Interop.Com2PropertyDescriptor.SetValue(Object component, Object value)
at MS.Internal.Data.PropertyPathWorker.SetValue(Object item, Object value)
at MS.Internal.Data.ClrBindingWorker.UpdateValue(Object value)
at System.Windows.Data.BindingExpression.UpdateSource(Object value)'
该应用程序是一个非常基本的 WPF 应用程序,其 ViewModel class 包含对工作簿的引用。然后我们有一个绑定到 ViewModel 的视图 (xaml) class。
视图模型 class:public class ViewModel
{
public Worksheet Worksheet { get; set; }
public ViewModel(Worksheet worksheet)
{
Worksheet = worksheet;
}
public string Name
{
get => Worksheet.Name;
set => Worksheet.Name = value;
}
}
然后,在视图中,我有一个绑定到工作表名称 属性 的 TextBox 控件。
基本上有两种方法可以做到这一点:
<TextBox Text="{Binding Worksheet.Name}"/>
或:
<TextBox Text="{Binding Name}"/>
那是因为我的 ViewModel 同时公开了工作表和名称。
这就是事情变得有趣的地方。
如果我使用第一种方法,它不起作用并且在 visual studio 的调试输出中我发现上面显示的错误。
如果我使用第二种方式,它工作得很好。这种方式是通过 Name 属性,这反过来意味着我有一个明确的
Worksheet.Name = ...
在我的代码中(在名字的 setter 中)。
这是我在两种解决方案之间看到的唯一区别。
进一步分析和问题
0x80028018 似乎是一个众所周知的;有几篇文章在谈论它。
我读了这个:
HowTo: Fix “Old format or invalid type library” error (0x80028018)
但是:
- 我无法解释为什么在我的应用程序中我看到两种不同的行为
- 我不知道如何解决应用程序级别的问题,以便绑定按预期工作
即使问题不是那么严重并且有一个简单的解决方法,也存在它可能指示更大问题的风险。这就是为什么我正在寻找一个强大的解决方案。
遗留代码,如果你想重现它除了 ViewModel class 之外,还有一个非常简单的 xaml 视图,(仅注意:作为 UserControl 实现,因为它必须加载到 ElementHost 中)。 有一个超文本框只是为了让第一个失去焦点并触发更新。
<UserControl x:Class="ExcelAddIn12.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ExcelAddIn12"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel>
<!--the non-working solution: -->
<TextBox Text="{Binding Worksheet.Name}"/>
<TextBox/>
</StackPanel>
</UserControl>
它只剩下启动和接线代码。作为启动器,我使用了带按钮的功能区(设计的,而不是 xaml)。
public partial class Ribbon1
{
private void Ribbon1_Load(object sender, RibbonUIEventArgs e) { }
private void button1_Click(object sender, RibbonControlEventArgs e)
{
Worksheet myWorksheet = (Worksheet)Globals.ThisAddIn.Application.ActiveSheet;
ViewModel vm = new ViewModel(myWorksheet);
UserControl1 view = new UserControl1() { DataContext = vm };
Form form = new Form();
ElementHost wpfHost = new ElementHost() { Dock = DockStyle.Fill, Child = view };
form.Controls.Add(wpfHost);
form.Show();
}
}
版本:
- Visual Studio 2017(英文)
- Excel 2010(英文)
您链接的文章说明如下:
Most of the Excel Object Model methods and properties require specifying an LCID (locale identifier). If a client computer has the English version of Excel, and the locale for the current user is configured for another language (e.g. German or French), Excel may fire the “Old format or invalid type library” exception with error code 0x80028018 (-2147647512).
它还告诉您将 System.Threading.Thread.CurrentThread.CultureInfo
设置为 Excel 使用的那个。最简单的方法是对当前 (UI) 线程全局执行一次:
Thread.CurrentThread.CurrentCulture = new CultureInfo(Application.LanguageSettings.LanguageID[MsoAppLanguageID.msoLanguageIDUI])
至于发生这种情况的原因,看起来在通过绑定直接调用和从包装器调用时使用了不同的 LCID 属性。在进行这些调用时,您可能需要检查 Thread.CurrentThread.CurrentCulture
和 Thread.CurrentThread.CurrentUICulture
属性。
这里有几个链接,可提供有关不同区域性设置的更多背景信息:
- Setting Culture (en-IN) globally in WPF application
- #763 – The Difference Between CurrentCulture and CurrentUICulture
- Application Culture / UICulture
- WPF Bindings and CurrentCulture Formatting
简而言之,CurrentThread.CurrentCulture
是您当前的区域设置(您可以通过控制面板更改),CurrentThread.CurrentUICulture
对应于您安装的 Windows 语言(您通常不能轻易更改),CultureInfo.DefaultThreadCurrentCulture
将更改当前的 CurrentThread.CurrentCulture
属性 并为未来的线程设置默认值,最后有类似于以下行的内容适用于 WPF 绑定:
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(
CultureInfo.CurrentCulture.IetfLanguageTag)));
当然,您也可以将本地用户的文化更改为 Excel 正在使用的文化(英语)。