I'm having trouble binding an IObservableMap to a ListView with C++/WinRT. My MainPage.xaml looks like this:
<ListView ItemsSource="{x:Bind TestCollection}">
</ListView>
where TestCollection
is a method with the signature winrt::Windows::Foundation::Collections::IObservableMap<hstring, hstring> TestCollection()
.
However, when running the app it crashes in the XAML setup code when setting the ItemsSource
property to TestCollection
with HRESULT: 0x80070057 (E_INVALIDARG)
, which doesn't really tell me much except that it doesn't like the map as a parameter.
I've consulted the docs on this matter and raised an issue with MS docs here because they are contradictory. To summarize:
The ItemsSource property docs say:
The ItemsSource property value must implement one of these interfaces:
IIterable<IInspectable>
IBindableIterable
first of which IObservableMap actually implements according to C++/WinRT headers while the C++/WinRT docs say:
If you want to bind a XAML items control to your collection, then you can.
But be aware that to correctly set the ItemsControl.ItemsSource property, you need to set it to a value of type IVector of IInspectable (or of an interoperability type such as IBindableObservableVector).
When replacing IObservableMap with IObservableVector everything works as expected.
I've also tried to do the equivalent thing in C# with a (non-observable) Dictionary and it worked just fine, which left me kinda puzzled. How does the CLR accomplish that? Is it converting the Dictionary to an IVector somewhere? It does not work with an IMap in C++, so there must be some sort of conversion going on, right?
Edit: after lots of wasted time and assembly-level debugging into Windows.UI.Xaml.dll
, I've found out following things:
QueryInterface
for IIterable<IInspectable>
IIterable<IKeyValuePair<K,V>>
which is an IInspectable
that doesn't mean you implement IIterable<IInspectable>
Is there a way to make this work at all?
I've thought about making a custom collection, but that'd mean I need to implement both IIterable<IKeyValuePair<K,V>>
and IIterable<IInspectable>
which I've tried for a moment, but that confused the compiler due to having two First()
methods and it not knowing which one to pick and me not knowing what to do.
Again, how does the CLR solve this?
Dictionary and it worked just fine, which left me kinda puzzled. How does the CLR accomplish that?
In C# Dictionary<TKey,TValue>
implements IEnumerable<KeyValuePair<TKey,TValue>>
For C++/CX there is Platform::Collections::Map this collection also implements all necessary interfaces.
And for C++/WinRT there is winrt::observable_map_base struct. That also implements all necessary interfaces.
Sample implementation from docs:
...
#include <iostream>
using namespace winrt;
using namespace Windows::Foundation::Collections;
...
struct MyObservableMap :
implements<MyObservableMap, IObservableMap<winrt::hstring, int>, IMap<winrt::hstring, int>, IMapView<winrt::hstring, int>, IIterable<IKeyValuePair<winrt::hstring, int>>>,
winrt::observable_map_base<MyObservableMap, winrt::hstring, int>
{
auto& get_container() const noexcept
{
return m_values;
}
auto& get_container() noexcept
{
return m_values;
}
private:
std::map<winrt::hstring, int> m_values{
{ L"AliceBlue", 0xfff0f8ff }, { L"AntiqueWhite", 0xfffaebd7 }
};
};
IObservableMap<winrt::hstring, int> map{ winrt::make<MyObservableMap>() };
for (auto const& el : map)
{
std::wcout << el.Key().c_str() << L", " << std::hex << el.Value() << std::endl;
}
IIterator<IKeyValuePair<winrt::hstring, int>> it{ map.First() };
while (it.HasCurrent())
{
std::wcout << it.Current().Key().c_str() << L", " << std::hex << it.Current().Value() << std::endl;
it.MoveNext();
}
User contributions licensed under CC BY-SA 3.0