1

I've got a WPF application that reads an XML file and creates an XDocument to bind to, passing its various elements down to other UI elements via DataContexts and such. The schema for the XML this application reads marks a few attributes on elements as entirely optional.

The method I'm using to bind is outlined in "How to: Bind to XDocument, XElement, or LINQ for XML Query Results". The application will load an XML file or create an empty XDocument, then assign it as the DataContext for the UI's main grid...

XDocument newDoc = new XDocument(new XElement("mydoc"));
gridMain.DataContext = newDoc.Root;

Instances of an element with the document are then passed as the ItemsSource for a ListBox...

<ListBox ItemsSource="{Binding Elements[item]}">
    <!-- ... -->
</ListBox>

And then, the DataTemplate for the items in the ListBox have icons and a ToggleButton whose states are defined by the value of a boolean relevant attribute on each <item> element. I wrote my own custom IValueConverter classes to handle conversion between XML boolean strings, and System.Windows.Visibility and bool values...

<ListBox.ItemTemplate>
    <DataTemplate>
        <DockPanel LastChildFill="True">
            <Grid Visibility="{Binding Attribute[relevant].Value, Converter={StaticResource BooleanToVisibilityConverter}}" Margin="2 0">
                <StaticResource ResourceKey="RelevantIcon"/>
            </Grid>
            <Grid Visibility="{Binding Attribute[relevant].Value, Converter={StaticResource NegatedBooleanToVisibilityConverter}}" Margin="2 0">
                <StaticResource ResourceKey="IrrelevantIcon"/>
            </Grid>
            <ToggleButton IsChecked="{Binding Attribute[relevant].Value, Converter={StaticResource BooleanConverter}}" VerticalAlignment="Center" Margin="2 0" Padding="5">Relevant</ToggleButton>
            <TextBox Text="{Binding Value}" TextWrapping="Wrap" AcceptsReturn="True" AllowDrop="True" Margin="2 0"/>
        </DockPanel>
    </DataTemplate>
</ListBox.ItemTemplate>

Now, if I load a document in where every <item> element has its relevant attribute defined, this works as expected. But, the schema defines that relevant may or may not be present. So, when I create an element or load in a document that's missing these attributes, the list item view completely breaks, silently. Both icons appear, and the toggle button seems to do absolutely nothing. Breakpoints reveal that my custom IValueConverters are never called for these elements, which indicates that the binding fails silently due to Attribute[relevant] being null, and null has no properties, let alone Value.

I've tried using FallbackValue in the Binding to no avail, and the null-conditional operator doesn't work in bindings - the XAML parser will exception at runtime.

The root of the problem seems to be that the bindings won't work if the attribute doesn't exist, and there doesn't seem to be a way to get it to have a default value or create the attribute if it doesn't exist. So, is there a way I can make the bindings behave the way I expect them to? If not, is there a way to add the attribute to each element if it's missing?

p0lar_bear
  • 2,203
  • 2
  • 21
  • 31
  • If there is no `Attribute[relevant]`, what is the `Visibility` or `IsChecked` properties that bind to this attribute supposed to be set to? – mm8 Jul 12 '19 at 14:47
  • The default value of `mydoc/item@relevant` as defined by the schema is supposed to be `false`. – p0lar_bear Jul 12 '19 at 14:50
  • But you don't bind to schema, are you? Did you try to set the `FallbackValue` and/or `TargetNullValue` of the bindings to `Collapsed` and `False` respecively? – mm8 Jul 12 '19 at 14:52
  • This lets me set the default state of the view (i.e. the icon visibility) should the attribute be missing, but clicking the ToggleButton does not cause the attribute to be created if it's missing, thus clicking it doesn't result in the icon changing as it should. – p0lar_bear Jul 12 '19 at 14:58
  • Why would the attribute be created when you click on the button...? – mm8 Jul 12 '19 at 14:59
  • The expected behavior I'm going for is that a lack of the attribute is to be treated as `false`. As per the schema, it might not always be there. If the button is clicked, the attribute should be set to `true`. Ergo, if the attribute is absent, the attribute needs to be created so it can be set accordingly. – p0lar_bear Jul 12 '19 at 15:07
  • Then you will have to implement this functionality obviously. – mm8 Jul 12 '19 at 15:07
  • Right. So... how? I'm looking for a way to gracefully handle missing optional attributes. Suppose I should mention, I'm used to working with JavaScript MV* libraries, where if a bound object is missing a property, it's treated as falsy and JS will just add the property if it's set. – p0lar_bear Jul 12 '19 at 15:17

0 Answers0