views:

871

answers:

2

I have a ListView in a Windows Form that I bind a list of objects to on the creation of the form. What I would like to do is on a button click loop through the items that were created and change their IsEnabled property to false. I've tried two methods and neither were particularly successful. Can anyone help fix these up and/or suggest an alternate method?

My ListView XAML

<ListView Margin="6" Name="myListView" ItemsSource="{Binding Path=.}">
   <ListView.ItemTemplate>
      <DataTemplate>
         <Grid>
            <Grid.ColumnDefinitions>
               <ColumnDefinition Width="10"/>
               <ColumnDefinition Width="350"/>
               <ColumnDefinition Width="20"/>
               <ColumnDefinition Width="350"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
               <RowDefinition Height="30" />
               <RowDefinition Height="30" />
               <RowDefinition Height="30" />
            </Grid.RowDefinitions>
            <TextBlock Name="ItemNameTextBlock" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="4" VerticalAlignment="Center" Text="{Binding Path=ItemName}" />
            <CheckBox Name="Action1CheckBox" Grid.Row="1" Grid.Column="1" Content="Action1" IsChecked="True" />
            <CheckBox Name="Action2CheckBox" Grid.Row="1" Grid.Column="3" Content="Action2" IsChecked="True" />
            <TextBox Height="23" Name="MyInputTextBox" Grid.Row="2" Grid.Column="1" Margin="2,0,2,0"  VerticalAlignment="Top" Width="25" Text="{Binding Path=DataValue}" />                                        
         </Grid>
      </DataTemplate>
   </ListView.ItemTemplate>
</ListView>

Goal: On button press (of an unrelated button) disable the CheckBoxes and the TextBox

Attempt 1: This didn't work, the Items are the databound items and I cannot figure out a way to get to the controls themselves to do something like this. Is this even possible?

foreach (var item in ReleaseDeployProcessListView.Items)
{
   ((CheckBox)item.FindControl("Action1CheckBox")).IsEnabled = false;
}

Attempt 2: I added a public property "IsFormElementsEnabled" to the Form and on the button click set this value to false. But I couldn't figure out how/if/what i needed to do to bind that to the items. I tried IsEnabled="{Binding Path=IsFormElementsEnabled} (which doesn't work since it is bound to the objects and that is not party of those obects) and I tried IsEnabled="{Binding Path=this.IsFormElementsEnabled} (which doesn't seem to work either)

+2  A: 

You could always add a boolean on your ViewModel and bind that to your CheckBox?

So imagine the following boolean on your View Model:

public bool CanEdit 
{ 
    get 
    {
        return canEdit;
    }
    set 
    {
        canEdit = value;
        NotifyPropertyChanged("CanEdit");
    }
}

public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
    if (this.PropertyChanged != null)
    {
        this.PropertyChanged(this, new PropertyChangedEventArgs(info));
    }
}

Also note that your ViewModel has to implement the INotifyPropertyChanged interface

Then bind this boolean to the CheckBox in your DataTemplate

<CheckBox Name="Action1CheckBox" Grid.Row="1" Grid.Column="1" Content="Action1" IsChecked="True" IsEnabled="{Binding CanEdit}" />

And in your for loop set the boolean to CanEdit is false:

foreach (var item in ReleaseDeployProcessListView.Items)
{
   item.CanEdit = false;
}
Arcturus
Yes this would definitely work, I was really hoping I could get one of the other two mechanisms to work though.
ChrisHDog
See my new answer ;) Although I still prefer this method though :)
Arcturus
+1  A: 

Ok here is how to make both your solutions work ;)

Attempt one:

foreach (var item in ReleaseDeployProcessListView.Items)
{
   ListViewItem i = (ListViewItem) ReleaseDeployProcessListView.ItemContainerGenerator.ContainerFromItem(item);

   //Seek out the ContentPresenter that actually presents our DataTemplate
   ContentPresenter contentPresenter = FindVisualChild<ContentPresenter>(i);

   CheckBox checkbox = (CheckBox)i.ContentTemplate.FindName("Action1CheckBox", contentPresenter);
   checkbox.IsEnabled = false;
}


private T FindVisualChild<T>(DependencyObject obj)
    where T : DependencyObject
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(obj, i);
        if (child != null && child is T)
            return (T)child;
    }

    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(obj, i);
        T childOfChild = FindVisualChild<T>(child);
        if (childOfChild != null)
            return childOfChild;
    }

    return null;
}

Attempt two:

Dependency property on your main control:

public bool IsFormElementsEnabled
{
    get { return (bool)GetValue(IsFormElementsEnabledProperty); }
    set { SetValue(IsFormElementsEnabledProperty, value); }
}

public static readonly DependencyProperty IsFormElementsEnabledProperty =
    DependencyProperty.Register("IsFormElementsEnabled", typeof(bool), typeof(YourClass), new PropertyMetadata(true));

Then use RelativeSource bindings to your main class in your CheckBox controls:

<CheckBox Name="Action1CheckBox" Grid.Row="1" Grid.Column="1" Content="Action1" IsChecked="True" 
          IsEnabled="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:YourControl}}, Path=IsFormElementsEnabled}" />

As you can see many roads lead to Rome ;) I would prefer to use the first option though, because it might be something part of your business logic for example a boolean that determines if a customer has already paid or something like that: CustomerHasPaid

Hope this helps

Arcturus
Thanks so much Arcturus, perfect answer to my question. I agree the other answer is probably better (and I've marked it as correct), but I was very curious as to how these other two methods could be accomplished. Thanks again!
ChrisHDog
No problem.. glad I could help ;)
Arcturus