c# - Excel/VSTO : error 0x80028018 only when binding directly to interop. - 提供的属性

标签 c# excel wpf vsto

当我尝试使用此测试应用程序设置 Worksheet 的 Name 属性时,由于 0x80028018 错误,它不起作用。
Test form
当我更改第一个文本框中的值并单击选项卡按钮时,什么也没有发生,而我希望 Sheet1 被重命名。在 Visual Studio 输出窗口中,我可以看到以下错误:

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 类包含对工作簿的引用。然后我们有一个绑定(bind)到 ViewModel 的 View (xaml) 类。
View 模型类:
public class ViewModel
{

    public Worksheet Worksheet { get; set; }
    public ViewModel(Worksheet worksheet)
    {
        Worksheet = worksheet;
    }

    public string Name
    {
        get => Worksheet.Name;
        set => Worksheet.Name = value;
    }
}
然后,在 View 中,我有一个绑定(bind)到 Worksheet 的 Name 属性的 TextBox 控件。
基本上有两种方法可以做到这一点:
<TextBox Text="{Binding Worksheet.Name}"/>
或者:
<TextBox Text="{Binding Name}"/>
那是因为我的 ViewModel 公开了 Worksheet 和 Name。
这就是事情变得有趣的地方。
如果我使用第一种方法,它不起作用,并且在 Visual Studio 的调试输出中我发现上面显示的错误。
如果我使用第二种方式,它工作得很好。这种方式是通过 Name 属性,这反过来意味着我有一个明确的Worksheet.Name = ...在我的代码中(在名称的 setter 中)。
这是我看到的两种解决方案之间的唯一区别。
进一步分析和问题
0x80028018 似乎是众所周知的;有几篇文章在谈论它。
我读了这个:
HowTo: Fix “Old format or invalid type library” error (0x80028018)
但:
  • 我无法解释为什么在我的应用程序中看到两种不同的行为
  • 我不知道如何在应用程序级别解决问题,以便绑定(bind)按预期工作

  • 即使问题不是那么严重并且有一个简单的解决方法,它也有可能表明更大的问题。这就是为什么我正在寻找一个强大的解决方案。
    剩下的代码,如果你想重现它
    除了 ViewModel 类之外,还有非常简单的 xaml View (仅注意:实现为 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 (EN)
  • Excel 2010 (EN)
  • 最佳答案

    您链接的文章指出以下内容:

    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])
    
    至于发生这种情况的原因,在通过绑定(bind)直接调用和从包装器属性调用时,似乎使用了不同的 LCID。您可能需要检查您的 Thread.CurrentThread.CurrentCultureThread.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属性并设置 future 线程的默认值,最后有类似以下行的内容,适用于 WPF 绑定(bind):
    FrameworkElement.LanguageProperty.OverrideMetadata(
        typeof(FrameworkElement),
        new FrameworkPropertyMetadata(
            XmlLanguage.GetLanguage(
                CultureInfo.CurrentCulture.IetfLanguageTag)));
    

    当然,您也可以将本地用户文化更改为 Excel 使用的一种(英语)。

    关于c# - Excel/VSTO : error 0x80028018 only when binding directly to interop. - 提供的属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62676434/

    相关文章:

    javascript - 如何在客户端将Javascript数组数据导出到excel

    c# - 如何更改 gridview 中组合框的可见性

    c# - 在 C# 中使用 RegEx 验证 float

    c# - 如何使用 SqlDataReader 获取列的数据类型和大小?

    c# - 如果在DataGridView单元格上存在

    c# - ASP.NET Core 2.2 MVC : wrap responses

    excel - 查找替换vbscript excel特定单元格

    c# - IDisposable 对象是否在使用 RequestContainer 的 Nancy 请求结束时被释放?

    vba - 计算所有工作表上不同范围内的总计

    wpf - 当前在 WPF 中编译像素着色器的最佳实践?