简体   繁体   中英

Datagrid itemsource twoway databinding not working

I try to build a wpf based application with datagrid and itemsource is bound to an object.

Xaml

   <DataGrid ItemsSource="{Binding Folders, Mode=TwoWay}" AutoGenerateColumns="False" HorizontalScrollBarVisibility="Visible"  VerticalScrollBarVisibility="Visible">
        <DataGrid.Columns>
            <DataGridTemplateColumn Header="Path" CanUserResize="False">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="260"></ColumnDefinition>
                                <ColumnDefinition Width="30"></ColumnDefinition>
                                <ColumnDefinition Width="30"></ColumnDefinition>
                            </Grid.ColumnDefinitions>
                            <TextBox Grid.Column="0" Text="{Binding Path}">
                                <TextBox.Style>
                                    <Style TargetType="{x:Type TextBox}">
                                        <Style.Triggers>
                                            <Trigger Property="IsMouseOver" Value="True">
                                                <Setter Property="Cursor" Value="Arrow"></Setter>
                                            </Trigger>
                                        </Style.Triggers>
                                    </Style>
                                </TextBox.Style>
                            </TextBox>
                        </Grid>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>

and the generated source code

   public partial class FolderControl : UserControl
    {
        public FolderViewModel FolderViewModel = new FolderViewModel("SourceFolders.xml");

        public FolderControl()
        {
            InitializeComponent();
            DataContext = FolderViewModel;
        }

        private TextBox GetTextBoxInstance(object obj)
        {
            var btn = (Button) obj;
            var parent = (Grid) btn.Parent;
            var text = (TextBox) parent.Children.Cast<UIElement>().Select(tb => tb).First();
            return text;
        }

        private void OpenFolder_OnClick(object sender, RoutedEventArgs e)
        {
            var text = GetTextBoxInstance(sender);
            Win32.FolderBrowserDialog fbd = new Win32.FolderBrowserDialog();
            Win32.DialogResult showDialog = fbd.ShowDialog();
            text.Text = fbd.SelectedPath;
        }

        private void OpenFile_OnClick(object sender, RoutedEventArgs e)
        {
            var text = GetTextBoxInstance(sender);
            Win32.OpenFileDialog ofd = new Win32.OpenFileDialog();
            if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                // If find the same file on collection, show msg error
                if (FolderViewModel.Folders.Any(file => file.Path == ofd.FileName))
                {
                    MessageBoxResult result = MessageBox.Show(EN.TEXT4, "Info", MessageBoxButton.OK); 
                }
                else
                {
                    text.Text = ofd.FileName;
                }
            }
        }

    }

and the viewModel(i not gonna show the whole code here, just what is necessary)

public class FolderViewModel : UploadViewModelBase
{
    public BindingList<Folder> Folders { get; set; }

    public FolderViewModel(string filename)
        : base(filename)
    {
    }

Here I use bindinglist generics type collection instead observecollection, for observe change on the collection.
And the last file, the model

public class Folder : ObservableObject
{
    private String _path;
    private OsType _os;
    private String _strOs;

    public String Path
    {
        get { return _path; }
        set { _path = value; RaisePropertyChanged(() => this.Path); }
    }

    public OsType Os
    {
        get { return _os; }
        set { _os = value; RaisePropertyChanged(() => this.Os); }
    }

    public String StrOs
    {
        get { return _strOs; }
        set { _strOs = value; RaisePropertyChanged(() => this.StrOs); }
    }
}

the whole problem is here, when I change the content from a row, it does not change on the collection object. What i do wrong?

You have had some confusion with data binding. Here are some tips:

Two-Way Binding s are used on the controls that display and can edit data bound values, not the container control's properties... this means that there is no point on setting Mode="Two-Way" on an ItemsSource property, because if you look at the ItemsControl.ItemsSource Property page on MSDN, you'll see that it only supports One-Way Binding s.

As mentioned, setting Mode="Two-Way" should only be used on controls that can display and can edit the data bound values. However if you look on the TextBox.Text property page on MSDN, you should see a section named Dependency Property Information . In this section, you can see that the Textbox.Text property binds Two-Way by default, so you never have to apply that setting to it.

So, while you say that your solution was to use the following:

<TextBox Grid.Column="0" Text="{Binding Path, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" ... />

I'm fairly sure that it was just the UpdateSourceTrigger property that you needed to set, because as we all know now, setting Mode=TwoWay on the Textbox.Text property does nothing:

<TextBox Grid.Column="0" Text="{Binding Path, UpdateSourceTrigger=PropertyChanged}" ... />

I can see at least the following two problems in your code:

  1. The DataGrid never modifies the ItemsSource reference itself (only its "contents"), therefore you should not set Mode=TwoWay .
  2. Most of WPF is designed to work with collection data types that implement INotifyCollectionChanged . BindingList<T> does not implement this interface. Therefore, you better use ObservableCollection<T> , which implements this interface.

Furthermore,

public BindingList Folders { get; set; }

seems to be incorrect, if your BindingList is this one: MSDN documentation for BindingList . It probably should be

public BindingList<Folder> Folders { get; set; }

But maybe it is just a typo in your question.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM