1
votes

I am attempting to reach this situation:

  • Have a ListBox with each cell containing a CheckBox and a TextBox, via DataTemplate
  • Make the list selectable, ie. I can bind the SelectedItems to a collection in my VM.
  • Link those selections to the status of the Checkbox(checked, unchecked).
  • Whenever the user types something in the TextBox, the Checkbox will be checked and selected, and vice versa, when the string is empty, it will be deselected.

I managed to get all of these criterias seperately like this:

  • Bind to SelectedItems using this solution.
  • Bind the CheckBox.IsChecked to:
    <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}" Path="IsSelected" Mode="OneWayToSource"/>
  • Bind the CheckBox.IsChecked to:
    <Binding Path="Text" ElementName="MyTextBox" Converter="{View:EmptyStringToBooleanConverter}" Mode="OneWay"/>

The thing is I can't get both of these bindings to work together. I have tried other solutions like DataTriggers, but they were not helpful because IsSelected is not accessible and because I need to bind to something inside the DataTemplate.
I am really trying to avoid having to add a property "IsSelected" to my class (represented by the DataTemplate).

Please help, I am willing to hear crazy suggestions as long as they're MVVM-y. Thanks!

2

2 Answers

0
votes

In general:

  • checkbox is unchecked by default
  • when you type string "checked", the checkbox will be checked, otherwise will stay unchecked

hope it helps.

XAML

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:clr="clr-namespace:WpfApplication1"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.Resources>
            <clr:StringToBooleanConverter x:Key="StringToBooleanConverter"/>
        </Grid.Resources>
        <ListBox ItemsSource="{Binding}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <CheckBox x:Name="chb" IsChecked="{Binding Text, ElementName=txt, Mode=OneWay, Converter={StaticResource StringToBooleanConverter}}"/>
                        <TextBox x:Name="txt" Text="{Binding Title, UpdateSourceTrigger=PropertyChanged}" Width="150"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>

Code-Behind

Imports System.Collections.ObjectModel

Class MainWindow
    Public Sub New()
        InitializeComponent()
        Me.DataContext = New ObservableCollection(Of DummyClass)(Enumerable.Range(0, 10).Select(Function(a) New DummyClass() With {.Title = "Dummy Title: " & a}))
    End Sub
End Class

Public Class DummyClass
    Property Title As String
End Class

Public Class StringToBooleanConverter
    Implements IValueConverter

    Public Function Convert(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.Convert
        Dim strValue = System.Convert.ToString(value)
        If String.IsNullOrEmpty(strValue) Then
            Return False 'Unchecked
        End If

        If strValue = "checked" Then
            Return True 'checked
        End If

        Return False 'default
    End Function

    Public Function ConvertBack(value As Object, targetType As Type, parameter As Object, culture As Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
        Throw New NotImplementedException()
    End Function
End Class
0
votes

This is the XAML code:

         <ListBox ItemsSource="{Binding MyList}" SelectionMode="Multiple" Width="200">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <DockPanel Margin="2">
                        <CheckBox DockPanel.Dock="Left" IsChecked="{Binding IsSelected}"/>
                        <TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" Background="Transparent"/>
                    </DockPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
            <ListBox.ItemContainerStyle>
                <Style TargetType="{x:Type ListBoxItem}">
                    <Setter Property="IsSelected" Value="{Binding IsSelected}"/>
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>

And its code behind:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        for (int i = 0; i < 100; i++)
        {
            MyList.Add(new ViewModel());
        }
    }
    //MyList Observable Collection
    public ObservableCollection<ViewModel> MyList { get { return _myList; } }
    private ObservableCollection<ViewModel> _myList = new ObservableCollection<ViewModel>();
}

ViewModel class (each item):

public class ViewModel : DependencyObject
{
    //Text Dependency Property
    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }
    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(string), typeof(ViewModel),
        new UIPropertyMetadata(null, (d, e) =>
        {
            ((ViewModel)d).IsSelected = !string.IsNullOrWhiteSpace((string)e.NewValue);
        }));
    //IsSelected Dependency Property
    public bool IsSelected
    {
        get { return (bool)GetValue(IsSelectedProperty); }
        set { SetValue(IsSelectedProperty, value); }
    }
    public static readonly DependencyProperty IsSelectedProperty =
        DependencyProperty.Register("IsSelected", typeof(bool), typeof(ViewModel),
        new UIPropertyMetadata(false, (d, e) =>
        {

        }));
}