1

My issue is receiving a DataContext which has 1 XmlElement, and passing it to a Converter. The converter requires the complete element, because it works off of multiple attributes. The problem is it receives MS.Internal.data.xmldatacollection, which contains one XmlElement, and I can't figure out how to proceed.

<UserControl x:Class="W3.Views.ComboView" ...>
    <ComboBox Style="{StaticResource ComboButtonStyle}" Width="auto" Height="auto"
         Text="{Binding XPath=.,
                        Converter={StaticResource valueFormattingConverter }}"                IsEditable="True" />
</UserControl>

The converter class:

[ValueConversion(typeof(XmlElement), typeof(string))]
public class ValueFormattingConverter : IValueConverter
{

public object Convert(object val, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
  if (targetType != typeof(string))
    throw new InvalidOperationException("The target must be a List<string>");
  if (val == null) return new List<string>();
  XmlElement e = null;

  if (val is string ) e = val as XmlElement;
  if (e == null) return null;

  return Information.XmlAccess.FormatXmlField(e);
}

So here, val is received as MS.Internal.data.xmldatacollection and I can't figure out how to deal with it to get the XmlElement it contains.

The vital context, which gets me the data: TOTAL

If we can fix it, the fix also needs to function in this more complex context:

<ContentControl x:Class="W3.Views.SimpleControlChooser"   >
    <ContentControl.Resources>
        <DataTemplate x:Key="combo" >
            <W3V:ComboView />
        </DataTemplate>
   // skipping other templates
    </ContentControl.Resources>
    <ContentControl.Style>
        <Style TargetType="{x:Type ContentControl}">
            <Setter Property="ContentTemplate" Value="{StaticResource combo}" />

            <Style.Triggers>
                <DataTrigger Binding="{Binding XPath=@format}" Value="check">
                    <Setter Property="ContentTemplate" Value="{StaticResource check}" />
                </DataTrigger>
                <DataTrigger Binding="{Binding XPath=@format}" Value="combo">
                    <Setter Property="ContentTemplate" Value="{StaticResource combo}" />
                </DataTrigger>

        //// skipping other DataTriggers

            </Style.Triggers>
        </Style>

    </ContentControl.Style>
</ContentControl>

Right, so where is the data specified? Going up a level:

<UserControl x:Class="W3.Views.SimpleControl" ...>
     ...
  <W3V:ControlLabel x:Name="Label"  FontSize="12"                     
                    Width="{Binding LabelWidth,
                    RelativeSource={RelativeSourceAncestorType=W3V:SimpleControl}}" />

     <W3V:SimpleControlChooser Content="{Binding}" />

I honestly don't understand the Content=line.

<ItemsControl  ItemsSource="{Binding XPath=*}" Margin="0,0,0,0">
  <ItemsControl.ItemTemplate>
    <DataTemplate>

      <W3V:SimpleControl x:Name="simple"  Content="{Binding}" 
            LabelWidth="{Binding LabelWidth,
                         RelativeSource={RelativeSource AncestorType=W3V:PropertyView}}" />


    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

This usage currently works, and I don't want to break this one to get the other to work. I AM open to writing conditional code in the Convert() method to adapt to the data if it needs to be different.

Alan Baljeu
  • 2,383
  • 4
  • 25
  • 40

1 Answers1

0

The problem has to do with the data binding, and not with the value converter.

Your data binding assumes that the object referenced by ComboBox.DataContext is a XmlElement instance. But this is not the case, as you say that the data context has a property of type XmlElement property instead.

This is when things get a bit tricky. Specifying both Path and XPath in the binding just doesn't work, because XPath is evaluated first. A valid workaround is binding the combo box's data context to the XmlElement itself:

<ComboBox
    DataContext="{Binding MyXmlElement}"
    Text="{Binding XPath=@value}" />

By the way, once the data binding is correct, there's no need for a converter in order to get the value of the value attribute displayed in the combo box.

Edited 03/11/2016

If the data context of the combo box's parent is an array of XmlElement instances instead of a single one, you can always point the right element in the binding by using the indexing operator:

<ComboBox
    DataContext="{Binding MyXmlElement[0]}"
    Text="{Binding XPath=@value}" />

Now, if you still need to bind to the XML element (really a XmlNode) instead of one of its attributes, then you can do that and use a converter:

<ComboBox
    DataContext="{Binding MyXmlElement[0]}"
    Text="{Binding XPath=., Converter={StaticResource ValueFormattingConverter}}" />

But then you need to fix your current converter, because it's declaring a conversion from XmlElement to string, and returning instead a List<string> instance.

In a nutshell, because XPath is evaluated before Path, you cannot use both in the ComboBox.Text data binding. A valid workaround involves binding the ComboBox.DataContext to the XmlElement of interest, then specifying only XPath in the ComboBox.Text binding.

Community
  • 1
  • 1
rucamzu
  • 926
  • 1
  • 9
  • 16
  • I cannot using Binding XPath=@value because I need the full XmlElement. EDIT: my mistake, I posted the wrong code because I was experimenting. Changed back to XPath=. ~~~~~ I cannot use DataContext="{Binding MyXmlElement}" because MyXmlElement is not a member variable but rather this class receives the element in an array as its datacontext. – Alan Baljeu Nov 03 '16 at 12:47
  • Could you post the code for your class' data context? – rucamzu Nov 03 '16 at 12:51
  • It's complicated because it's a compound control operating all on the same data, but okay. – Alan Baljeu Nov 03 '16 at 12:57
  • Details added. I warned it was complicated! – Alan Baljeu Nov 03 '16 at 13:14
  • @AlanBaljeu Never mind how complicated, as long as you know what the inherited data context of your `ComboBox` is, you can refine it with an explicit binding and work your way around the issues that come from `XPath` being evaluated before `Path`. – rucamzu Nov 03 '16 at 20:57