When I try to set Worksheet's Name property using this test application, it doesn't work due to an 0x80028018 error.
When I change the value in the first text box, and I click the tab button, nothing happens, while I expect the Sheet1 to be renamed. In the Visual Studio Output Window I can see the following error:
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)'
The application is a very basic WPF application with a ViewModel class that contains the reference to the Workbook. Then we have a view (xaml) class that binds to the ViewModel.
Viewmodel 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;
}
}
Then, in the view, I have a TextBox control that is bound to the Name property of the Worksheet.
Basically there are two ways of doing this:
<TextBox Text="{Binding Worksheet.Name}"/>
or:
<TextBox Text="{Binding Name}"/>
that's because my ViewModel exposes both Worksheet and Name.
And this is where things get interesting.
If I use the first way, it doesn't work and in the debug output of visual studio I find the error shown above.
If I use the second way it works just fine. This way is through Name property, that in turn means that I have an explicit
Worksheet.Name = ...
in my code (in the Name's setter).
This is the only difference that I see between the two solutions.
The 0x80028018 seems to be a well-known one; there are several articles talking about it.
I read this one:
HowTo: Fix “Old format or invalid type library” error (0x80028018)
but:
Even if the problem is not so critical and there is an easy workaround for it, there is the risk that it could indicate greater issues. That's why I'm looking for a robust solution.
Left over code, if you want to reproduce itBeyond the ViewModel class there is the very easy xaml view, (only note: implemented as a UserControl because it has to be loaded into an ElementHost). There is an exceeding TextBox just for allowing the first to lose focus and trigger the update.
<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>
It remains only the launch & wiring code. As a launcher i used a Ribbon (designed, not xaml) with a button.
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();
}
}
Versions:
The article you linked states the following:
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).
It also tells you to set the System.Threading.Thread.CurrentThread.CultureInfo
to the one Excel uses. The simplest way to do this is to do it once globally for the current (UI) thread:
Thread.CurrentThread.CurrentCulture = new CultureInfo(Application.LanguageSettings.LanguageID[MsoAppLanguageID.msoLanguageIDUI])
As for the reason this is happening, it looks like a different LCID is used when calling directly through binding and when calling from your wrapper property. You might want to check your Thread.CurrentThread.CurrentCulture
and Thread.CurrentThread.CurrentUICulture
properties when those calls are made.
Here are a couple of links for a bit more background information about the different culture settings:
In short, CurrentThread.CurrentCulture
is your current region setting (that you can change via control panel), CurrentThread.CurrentUICulture
corresponds to the language of Windows that you installed (that you usually cannot change easily), CultureInfo.DefaultThreadCurrentCulture
will change the current CurrentThread.CurrentCulture
property and setup the default value for future threads and finally there is something like the following line, that applies to WPF bindings:
FrameworkElement.LanguageProperty.OverrideMetadata(
typeof(FrameworkElement),
new FrameworkPropertyMetadata(
XmlLanguage.GetLanguage(
CultureInfo.CurrentCulture.IetfLanguageTag)));
Of course you could also just change your local users culture to the one Excel is using (Englisch).
User contributions licensed under CC BY-SA 3.0