views:

119

answers:

2

Hello,

I have a MeetingViewModelList bound to a DataGrid. Each MeetingViewModel has a DocumentViewModelList bound to a ListBox within the DataGrid`s DataGridTemplateColumn. alt text

The IsSelected property of the DocumentViewModel is bound to the ListBox`s Item property IsSelected.

I get no binding errors in the output console.

The delete document button in the DocumentViewModel checks in its CanExecute Method this:

private bool CanDeleteDocument()
        {
            return _isSelected;
        }

When I select the FIRST Item in the ListBox, the Delete button is enabled. When I selected the 2nd,3rd etc. Item in the ListBox the Delete button is ALWAYS disabled.

I try to paste only the important code and cropped other stuff:

I have just tried to rebuild the scenario with just a ListBox -not being part of a DataGrid- and I get the same behavior :/

I would be pleased about any hint :)

XAML:

<DataGrid  VirtualizingStackPanel.VirtualizationMode="Recycling"
                ScrollViewer.CanContentScroll="False"                  
                CanUserResizeRows="True"                
                VerticalScrollBarVisibility="Auto"
                ItemsSource="{Binding MeetingViewModelList}"
                AutoGenerateColumns="False" 
                x:Name="DailyGrid" 
                Height="580"
                SelectionMode="Single"
                CanUserSortColumns="False"
                Background="#FF2DCE2D"               
                CanUserAddRows="False" 
                HeadersVisibility="All"
                RowHeaderWidth="40"
                RowHeight="200" >                        


                        <!--Content-->
                        <DataGridTemplateColumn Width="0.5*" Header="Content">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <Helper:RichTextBox LostFocus="RTFBox_LostFocus" VerticalScrollBarVisibility="Auto" x:Name="RTFBox" Text="{Binding Content,IsAsync=True}" AcceptsReturn="True" AutoWordSelection="False" AllowDrop="False" SelectionBrush="#FFAC5BCB" HorizontalScrollBarVisibility="Hidden">
                                        <Helper:RichTextBox.TextFormatter>
                                            <Helper:RtfFormatter />
                                        </Helper:RichTextBox.TextFormatter>
                                    </Helper:RichTextBox>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>

                        <!--Documents-->
                        <DataGridTemplateColumn Visibility="{Binding Source={StaticResource spy}, Path=DataContext.DocumentsVisible}" IsReadOnly="True" Width="125" Header="Attachments">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>                                    
                                        <StackPanel Background="Green" DataContext="{Binding DocumentViewModelList}" Orientation="Vertical" >
                                            <ListBox SelectionMode="Single" VirtualizingStackPanel.IsVirtualizing="False"
                                                Height="100"                                               
                                                Width="Auto"
                                                Focusable="True"
                                                ScrollViewer.HorizontalScrollBarVisibility="Auto" 
                                                ScrollViewer.VerticalScrollBarVisibility="Auto" 
                                                Grid.Row="1" 
                                                Name="documentListBox"
                                                BorderThickness="1"                                                
                                                ItemsSource="{Binding}"
                                                Visibility="{Binding ElementName=documentListBox,Path=HasItems, Converter={StaticResource boolToVisibilityConverter}}"
                                                >
                                                <ListBox.ItemTemplate>
                                                    <DataTemplate>
                                                        <StackPanel>                                                          
                                                            <TextBlock Text="{Binding Path=Name}" />
                                                        </StackPanel>
                                                    </DataTemplate>
                                                </ListBox.ItemTemplate>
                                                <ListBox.ItemContainerStyle>                                                  
                                                        <Style TargetType="{x:Type ListBoxItem}">
                                                            <Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}" />                                                      
                                                        </Style>      
                                                </ListBox.ItemContainerStyle>                                        
                                            </ListBox>
                                            <StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
                                                <Button Command="{Binding Path=DeleteDocumentCommand}" HorizontalAlignment="Stretch" Content="Delete" />
                                                <Button Command="{Binding Path=AddDocumentCommand}" HorizontalAlignment="Stretch" Content="Add" />
                                                <Button Command="{Binding Path=OpenDocumentCommand}" HorizontalAlignment="Stretch" Content="Open" />                                             
                                            </StackPanel>
                                        </StackPanel>                                  
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>

ReportingViewModel(Controller):

public class ReportingViewModel : ViewModelBase
    {   
        private ObservableCollection<MeetingViewModel> _meetingViewModelList;      

        public ReportingViewModel ()
        {            

        }  

        public ObservableCollection<MeetingViewModel> MeetingViewModelList
        {
            get { return _meetingViewModelList; }
            set
            {
                _meetingViewModelList= value;
                this.RaisePropertyChanged("MeetingViewModelList");
            }
        }         
    }

MeetingViewModel:

public class MeetingViewModel: ViewModelBase
{
    private ObservableCollection<DocumentViewModel> _documentViewModelList = new ObservableCollection<DocumentViewModel>();
    private Meeting _meeting;

    public MeetingViewModel(Meeting meeting)
    {
        _meeting= meeting;

        _meeting.Documents.ForEach(doc => DocumentViewModelList.Add(new DocumentViewModel(doc)));                                   
    }

    public ObservableCollection<DocumentViewModel> DocumentViewModelList
    {
        get { return _documentViewModelList; }
        set
        {
            _documentViewModelList = value;
            this.RaisePropertyChanged("DocumentViewModelList");
        }
    } 

    public string Content
    {
        get { return _meeting.Content; }
        set
        {
            if (_meeting.Content == value)
                return;

            _meeting.Content = value;
            this.RaisePropertyChanged("Content");
        }


   }     
    }

DocumentViewModel:

public class DocumentViewModel : ViewModelBase
{
    private Document _document;

    private RelayCommand _deleteDocumentCommand;
    private RelayCommand _addDocumentCommand;
    private RelayCommand _openDocumentCommand;

    public DocumentViewModel(Document document)
    {
        _document = document;
    }

    private void DeleteDocument()
    {
        throw new NotImplementedException();
    }

    private bool CanDeleteDocument()
    {
        return _isSelected;
    }

    private void AddDocument()
    {

    }

    private void OpenDocument()
    {

    }

    public RelayCommand DeleteDocumentCommand
    {
        get { return _deleteDocumentCommand ?? (_deleteDocumentCommand = new RelayCommand(() => DeleteDocument(), () => CanDeleteDocument())); }
    }

    public RelayCommand AddDocumentCommand
    {
        get { return _addDocumentCommand ?? (_addDocumentCommand = new RelayCommand(() => AddDocument())); }
    }

    public RelayCommand OpenDocumentCommand
    {
        get { return _openDocumentCommand ?? (_openDocumentCommand = new RelayCommand(() => OpenDocument())); }
    }

    private bool _isSelected;
    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            if (_isSelected == value)
                return;

            _isSelected = value;
            this.RaisePropertyChanged("IsSelected");
        }
    }

    public string Name
    {
        get { return _document.DocumentName; }
        set
        {
            if (_document.DocumentName == value)
                return;

            _document.DocumentName = value;
            this.RaisePropertyChanged("Name");
        }
    }       
}
A: 

I'm sure it can be made to work this way, but wouldn't it be easier to just track the selected item in another way? For example, binding to an ICollectionView (for example ListCollectionView) that wraps the actual collection allows the use of the built-in selection tracking mechanism (CurrentItem of the ICollectionView). Alternatively, you could use SelectedValue and SelectedValuePath of the ListBox.

Alex Paven
Hm... not that I want to dispose now the IsSelected idea, but what you say Alex is right. I was so focused on IsSelected with ViewModel. I made now this: SelectedItem="{Binding SelectedDocumentViewModel,Mode=TwoWay}" and in the DocumentViewModel:private bool CanDeleteDocument() { if(SelectedDocumentViewModel != null) return true; else return false; } that works, but I would like to know why the IsSelected does not work! ;-)
Lisa
I would guess that it's because the IsSelected property of the ListBoxItem is manipulated internally by the ListBox and that disturbs the binding, but I could be way off. Not really sure.
Alex Paven
A: 

I think the problem is that delete command does not know that selection has changed.

Add CanDeleteChanged to the relay command and raise it after this.RaisePropertyChanged("IsSelected"). I've had similar problems with Prism apps before.

EDIT. Infact you should add the CanDeleteChanged and put a breakpoint in it and see if its being called when you expect it to or not anyway.

Sorry I meant CanExecuteChanged event on the Delete relay command. Your code should have the declaration of a relay command somewhere. For the sake of info here it is

public class RelayCommand : ICommand { #region Fields

readonly Action<object> _execute;
readonly Predicate<object> _canExecute;        

#endregion // Fields

#region Constructors

public RelayCommand(Action<object> execute)
: this(execute, null)
{
}

public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
    if (execute == null)
        throw new ArgumentNullException("execute");

    _execute = execute;
    _canExecute = canExecute;           
}
#endregion // Constructors

#region ICommand Members

[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
    return _canExecute == null ? true : _canExecute(parameter);
}

public event EventHandler CanExecuteChanged
{
    add { CommandManager.RequerySuggested += value; }
    remove { CommandManager.RequerySuggested -= value; }
}

public void Execute(object parameter)
{
    _execute(parameter);
}

#endregion // ICommand Members

}

NVM
sorry... but how can I add a property to the RelayCommand? do you have me please a code snippet, I have no idea how the code must look like.
Lisa
Another problem is that the Button is enabled although the documentListBox is empty... I think this problem is due to the fact that the delete button is in the DocumentViewModel. As there is no currentMeeting.DocumentList.Count > 0 I could bind to as I have to enable/disable the deletebutton(s) when the whole meetings are loaded. Am I right I should move the delete and other buttons to the controller? But even then I have only ONE buttoncommand at the controller but in the view I have 10 meetings each has a delete document button. Does not work...
Lisa
seems I have to put the buttons in a DocumentListViewModel like they do it in WAF for wpf!?
Lisa
ok I made now the DocumentListViewModel and it what a hell of trial and error with the binding. But now I have all working! Delete button is disabled if the DocumentList is empty or any of the Documents is not selected COOL , I should start a blog :P
Lisa
Sorry havent checked SO for the last couple of days. For CanDeleteChanged you should look at the constructor parameters of RelayCommand.
NVM
CanDelecteDocument is a METHOD how could I raise that?
Lisa
I changed CanDeleteDocument to a property and raised it after the IsSelected or SelectedDocumentViewModel(have changed some stuff and it seems in that scenario the selected item is not working too, because the button is always gray...) therefore I switched back to the former scenario where the SelectedDocumentViewModel worked. Thats sad but true :/
Lisa