3

I really don't understand WPF and XAML, and inherited some terribly written code, so I may be butchering this, but here goes.

I inherited a DataGrid bound (in code behind) to a list of Person objects, where the necessary DataGridTextColumns are specified in XAML (presumably to allow styling).

<DataGrid x:Name="PersonGrid" ItemsSource="{Binding}" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Path=LastName}" MaxWidth="Infinity" MinWidth="150" Header="Last Name">
        <DataGridTextColumn Binding="{Binding Path=FirstName}" MaxWidth="Infinity" MinWidth="150" Header="First Name" />
        <DataGridTextColumn Binding="{Binding Path=DOB, StringFormat=\{0:MM/dd/yyyy\}}" MaxWidth="Infinity" MinWidth="200" Header="Date of Birth (MM/DD/YYYY)" />
        <DataGridTextColumn Binding="{Binding Path=Number}" MaxWidth="Infinity" MinWidth="150" Header="(P)erson Number" />
    </DataGrid.Columns>
    <DataGrid.DataContext>
        <dm:Person />
    </DataGrid.DataContext>
</DataGrid>

I would like to display just the person's last initial, optionally based on the state of a checkbox.

<CheckBox Name="ShowFullNames_CheckBox" Content="Show Full Names" IsChecked="False"/>

I was able to hook up a converter to the LastName Binding in code behind, but get an error ("Binding cannot be changed after it has been used.") when I try to change that converter after the data is bound.

I thought that maybe I could also bind the checkbox IsChecked state to the ConverterParameter or one binding of a Multibinding, but couldn't get that to work.

<DataGridTextColumn MaxWidth="Infinity" MinWidth="150" Header="Last Name">
    <DataGridTextColumn.Binding>
        <MultiBinding Converter="myStringTruncator">
            <Binding Source="ShowFullNames_CheckBox" Path="IsChecked"/>
            <Binding Path="LastName"/>
        </MultiBinding>
    </DataGridTextColumn.Binding>
</DataGridTextColumn>

In the Convert method of myStringTruncator, the first binding was just filled with DependencyProperty.UnsetValue, instead of the value of the checkbox.

There's probably a really simple way to do this that I'm not seeing. Any ideas?

Michael Repucci
  • 1,633
  • 2
  • 19
  • 35
  • As you see from the answer, the problem was with a binding. To get a better understanding of what the bindings are doing, you can set `PresentationTraceSources.TraceLevel="High"` on the binding and it will show you a bunch of useful information (and it will have probably pointed out it didn't find a `Source` called `ShowFullNames_CheckBox`). This is a very good way for debugging problems, especially for inherited applications ;-) – RoelF Mar 12 '14 at 15:57

1 Answers1

4

You can add the Converter to the Binding in XAML.

<DataGridTextColumn Binding="{Binding Path=LastName, Converter={StaticResource YourConverter}"
                    MaxWidth="Infinity"
                    MinWidth="150"
                    Header="Last Name">

But for binding the state of the checkbox you would have to use something like this (untested)

<DataGridTextColumn Header="Last Name">
  <DataGridTextColumn.Binding>
    <MultiBinding Converter="{StaticResource NameAndCheckBoxMultiValueConverter}">
      <Binding Path="LastName" />
      <Binding ElementName="myCheckBox" Path="IsChecked" />
    </MultiBinding>
  </DataGridTextColumn.Binding>
 </DataGridTextColumn>

And the converter:

  using System;
    using System.Globalization;
    using System.Windows.Data;

    namespace TestWpf
    {
        public class NameAndCheckBoxMultiValueConverter: IMultiValueConverter
        {
            public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
            {
            var lastName = values[0] as String;
            if(lastName != null)
            {
                var isChecked = (bool)values[1];
                if (isChecked)
                {
                    return lastName.Substring(0,1);
                }
                return lastName;
            }
            return null;
            }

            public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }
    }
user3411327
  • 1,031
  • 8
  • 14
  • You replied while I was editing, but hit the nail on the head. The only change I needed after my edits was to use ElementName instead of Source on the Multibinding Binding to the checkbox. Thanks! – Michael Repucci Mar 12 '14 at 15:47
  • Thank you also for the thoroughness of your reply! Those details really help me understand this better. – Michael Repucci Mar 12 '14 at 15:48
  • Actually, while you're at it, I've had trouble specifying the converter as a static resource, and instead assign it in code behind. I'd like to use XAML instead, but I can't seem to make it work correctly. My DataGrid isn't in a Window, but rather in a UserControl, so setting Window.Resources as MSDN suggests doesn't work. Do you know how I can make my converter a static resource within the user control? – Michael Repucci Mar 12 '14 at 15:53
  • 1
    You can put it in the . For answering the topic: Clone a Binding to assign a converter at runtime http://stackoverflow.com/a/14740448 – user3411327 Mar 12 '14 at 15:56
  • Pardon me for needing it spelled out, but I couldn't figure out how to add it to as the tag as suggested here (http://msdn.microsoft.com/en-us/library/system.windows.data.multibinding%28v=vs.110%29.aspx) didn't work. What's the correct tag and attribute? – Michael Repucci Mar 12 '14 at 16:10
  • 1
    You need the namespace declaration in the file header like: xmlns:testWpf="clr-namespace:TestWPF". Then add converter: . Does that help? – user3411327 Mar 13 '14 at 11:29
  • Mostly there. Initially I had MyMultiValueConverter class inside the UserControl class, and from there I couldn't seem to access it in the UserControl.Resources. But if I moved it outside the UserControl class to the namespace in which the UserControl class also sits, then what you suggested works exactly. Could you explain why it won't work as a subclass of the UserControl class? – Michael Repucci Mar 13 '14 at 14:47
  • 1
    I have never nested a converter inside another class. Just found, that is not possible: http://stackoverflow.com/a/4270348/3411327 – user3411327 Mar 13 '14 at 15:17
  • I'm pulling my hair out. This code is working on 3 computers in the office, but I installed to 4 computers in the field and it fails. The CheckBox isn't binding to the multibinding, so I keep getting a type MS.Internal.NamedObject that I suspect is some default unbound binding object. I can't figure any difference between these computers, and don't know how to track down the issue. Any thoughts about why the binding might not be sticking? – Michael Repucci Mar 19 '14 at 21:46
  • Post on this here, http://stackoverflow.com/questions/22536645/what-hardware-platform-difference-could-cause-an-xaml-wpf-multibinding-to-checkb, and got the answer. It's a bug in .NET Framework 4. Installing .NET 4.5.1 solves this problem. – Michael Repucci Mar 20 '14 at 15:46