Reputation: 132
I'm trying to clear up some squigglies in our project's XAML. The issue I'm facing is that we use dictionaries that use a custom type as the key rather than string. This works fine at runtime, but the XAML editor squigglies the uses of them with the error:
Type 'Dictionary`2' is not a Collection
Example:
<Window x:Class="DictionaryCustomKey.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:DictionaryCustomKey"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800" Height="450"
DataContext="{x:Static local:MainWindow.TheData}"
mc:Ignorable="d">
<StackPanel>
<ListView ItemsSource="{Binding StringKeyDictionary}" />
<TextBlock Text="String First is:" />
<TextBlock Text="{Binding StringKeyDictionary[First]}" />
<ListView ItemsSource="{Binding MyKeyDictionary}" />
<TextBlock Text="MyKey First is:" />
<TextBlock Text="{Binding MyKeyDictionary[First]}" />
</StackPanel>
</Window>
The very last instance of "First" is squigglied.
Here's the codebehind where I try everything I can imagine to make the editor happy that the key type "MyKey" is suitable as a Dictionary key:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
namespace DictionaryCustomKey
{
public class Data
{
public class MyKeyTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string)
{
return new MyKey((string)value);
}
return null;
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (value == null)
{
return null;
}
if (destinationType == typeof(string))
{
return ((MyKey)value).String;
}
return null;
}
}
[TypeConverter(typeof(MyKeyTypeConverter))]
public class MyKey : IComparable, IComparable<MyKey>, IComparable<string>, IEquatable<MyKey>, IEquatable<string>, IConvertible
{
public MyKey(string stringKey)
{
m_String = stringKey;
}
public static implicit operator MyKey(string v)
{
return new MyKey(v);
}
public static implicit operator string(MyKey v)
{
return v.m_String;
}
public String String => m_String;
private String m_String;
public override string ToString()
{
return String;
}
public override bool Equals(object obj)
{
if (obj is MyKey)
{
return ((MyKey)obj) == this;
}
if (obj is string)
{
return ((string)obj) == this.String;
}
return false;
}
public bool Equals(MyKey other)
{
return this == other;
}
public override int GetHashCode()
{
return String.GetHashCode();
}
public int CompareTo(object obj)
{
return String.CompareTo(obj);
}
public int CompareTo(MyKey other)
{
return String.CompareTo(other);
}
public int CompareTo(string other)
{
return String.CompareTo(other);
}
public bool Equals(string other)
{
return String.Equals(other);
}
public TypeCode GetTypeCode()
{
return String.GetTypeCode();
}
public bool ToBoolean(IFormatProvider provider)
{
return ((IConvertible)String).ToBoolean(provider);
}
public char ToChar(IFormatProvider provider)
{
return ((IConvertible)String).ToChar(provider);
}
public sbyte ToSByte(IFormatProvider provider)
{
return ((IConvertible)String).ToSByte(provider);
}
public byte ToByte(IFormatProvider provider)
{
return ((IConvertible)String).ToByte(provider);
}
public short ToInt16(IFormatProvider provider)
{
return ((IConvertible)String).ToInt16(provider);
}
public ushort ToUInt16(IFormatProvider provider)
{
return ((IConvertible)String).ToUInt16(provider);
}
public int ToInt32(IFormatProvider provider)
{
return ((IConvertible)String).ToInt32(provider);
}
public uint ToUInt32(IFormatProvider provider)
{
return ((IConvertible)String).ToUInt32(provider);
}
public long ToInt64(IFormatProvider provider)
{
return ((IConvertible)String).ToInt64(provider);
}
public ulong ToUInt64(IFormatProvider provider)
{
return ((IConvertible)String).ToUInt64(provider);
}
public float ToSingle(IFormatProvider provider)
{
return ((IConvertible)String).ToSingle(provider);
}
public double ToDouble(IFormatProvider provider)
{
return ((IConvertible)String).ToDouble(provider);
}
public decimal ToDecimal(IFormatProvider provider)
{
return ((IConvertible)String).ToDecimal(provider);
}
public DateTime ToDateTime(IFormatProvider provider)
{
return ((IConvertible)String).ToDateTime(provider);
}
public string ToString(IFormatProvider provider)
{
return String.ToString(provider);
}
public object ToType(Type conversionType, IFormatProvider provider)
{
return ((IConvertible)String).ToType(conversionType, provider);
}
public static bool operator ==(MyKey lhs, MyKey rhs)
{
return lhs.String == rhs.String;
}
public static bool operator !=(MyKey lhs, MyKey rhs)
{
return !(lhs == rhs);
}
}
public Dictionary<string, object> StringKeyDictionary { get; set; } = new Dictionary<string, object>
{
{"First", "zero"},
{"Second", "one"}
};
public Dictionary<MyKey, object> MyKeyDictionary { get; set; } = new Dictionary<MyKey, object>
{
{new MyKey("First"), "zero"},
{new MyKey("Second"), "one"}
};
}
public partial class MainWindow : Window
{
static public Data TheData { get; set; } = new Data();
public MainWindow()
{
InitializeComponent();
}
}
}
But nothing seems to work.
Edit: An answer is given that makes the squiggly go away, but it reveals there is actually a worse problem: squiggly or not, the designer has trouble displaying the binding: https://developercommunity.visualstudio.com/content/problem/848331/visual-studio-must-be-restarted-after-every-build.html
Upvotes: 2
Views: 325
Reputation: 2509
This appears to be a bug in the XAML editor. Using nested element syntax doesn't have the error:
<TextBlock>
<TextBlock.Text>
<Binding Path="MyKeyDictionary[First]" />
</TextBlock.Text>
</TextBlock>
I would recommend reporting it through the Visual Studio feedback tool.
Upvotes: 2