如何调用UpdateSource()来显示DataGrid上的显式绑定?

时间:2021-05-20 19:37:13

I have a DataGrid that contains information from a settings object. Currently there is two-way binding between the DataGrid and settings object. However, I want to put a "Save" button in that only binds changes made in the DataGrid to the object if the user clicks the Save button. However, I'm not sure how to call UpdateSource() for my particular case with my DataGrid.

我有一个DataGrid包含来自设置对象的信息。目前,DataGrid和设置对象之间存在双向绑定。但是,我想在其中放置一个“保存”按钮,只有当用户单击“保存”按钮时,才会将DataGrid中所做的更改绑定到对象。但是,我不知道如何使用我的DataGrid为我的特定情况调用UpdateSource()。

Here is my xaml.cs code:

这是我的xaml.cs代码:

public void LoadDataFields(Data d)
        {
            Grid1.ItemsSource = d.Fields;
        }

private void SaveChanges(object sender, RoutedEventArgs e)
        {
            BindingExpression be = Grid1.GetBindingExpression(DataGrid.ItemsSourceProperty);
            be.UpdateSource();
        }

Here is my xaml code:

这是我的xaml代码:

<DataGrid x:Name="Grid1" 
                  IsReadOnly="False" 
                  Height="360" 
                  Margin="20,15,20,15" 
                  VerticalAlignment="Top" 
                  AutoGenerateColumns="False" 
                  CanUserAddRows="False" SelectionUnit="Cell"
                  ItemsSource="{Binding data}"
                  >

            <DataGrid.Columns>
                <DataGridTemplateColumn Header="Field">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBox Text="{Binding Path=name, Mode=TwoWay, UpdateSourceTrigger=Explicit}"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Header="Length of Field">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBox Text="{Binding Path=length, Mode=TwoWay, UpdateSourceTrigger=Explicit}"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>

Is there an easy way to call UpdateSource() so that the binding only takes place if the Save button is clicked? My guess is that I'm just putting the wrong property in for the the GetBindingExpression method.

是否有一种简单的方法来调用UpdateSource(),以便仅在单击“保存”按钮时才进行绑定?我的猜测是我只是为GetBindingExpression方法添加了错误的属性。

2 个解决方案

#1


4  

Yes, there is a way, but it is not a very very easy way. First of all you need 2 helper methods:

是的,有办法,但这不是一个非常简单的方法。首先,您需要2个辅助方法:

public static T GetVisualChild<T>(Visual parent) where T : Visual
{
    Visual visual;
    T child = default(T);

    int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
    for (int i = 0; i < childrenCount; i++)
    {
        visual = (Visual)VisualTreeHelper.GetChild(parent, i);
        child = visual as T;
        if (child == null)
        {
            child = GetVisualChild<T>(visual);
        }
        if (child != null)
        {
            break;
        }
    }
    return child;
}

public T FindVisualChild<T>(DependencyObject obj, string name) where T : DependencyObject
{
    DependencyObject child;
    FrameworkElement frameworkElement;
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
    {
        child = VisualTreeHelper.GetChild(obj, i);
        frameworkElement = child as FrameworkElement;
        if (child != null && child is T && frameworkElement != null && frameworkElement.Name == name)
        {
            return (T)child;
        }
        else
        {
            T childOfChild = FindVisualChild<T>(child, name);
            if (childOfChild != null)
            {
                return childOfChild;
            }
        }
    }

    return null;
}

Then you have to give a name to your binded controls. For example "textBox":

然后你必须给你的绑定控件命名。例如“textBox”:

<DataGridTemplateColumn Header="Field">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBox x:Name="textBox" Text="{Binding Path=name, Mode=TwoWay, UpdateSourceTrigger=Explicit}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Length of Field">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBox x:Name="textBox" Text="{Binding Path=length, Mode=TwoWay, UpdateSourceTrigger=Explicit}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

Then in your SaveChanges method, you can use this code:

然后在SaveChanges方法中,您可以使用以下代码:

private void SaveChanges(object sender, RoutedEventArgs e)
{
    DataGridRow dataGridRow;
    DataGridCellsPresenter dataGridCellsPresenter;
    DataGridCell dataGridCell;
    TextBox textBox;
    BindingExpression bindingExpression;

    for (int i = 0; i < Grid1.Items.Count; i++)
    {
        dataGridRow = (DataGridRow)Grid1.ItemContainerGenerator.ContainerFromIndex(i);
        dataGridCellsPresenter = GetVisualChild<DataGridCellsPresenter>(dataGridRow);
        for (int j = 0; j < Grid1.Columns.Count; j++)
        {
            dataGridCell = (DataGridCell)dataGridCellsPresenter.ItemContainerGenerator.ContainerFromIndex(j);
            textBox = FindVisualChild<TextBox>(dataGridCell, "textBox");
            if (textBox != null)
            {
                bindingExpression = BindingOperations.GetBindingExpression(textBox, TextBox.TextProperty);
                bindingExpression.UpdateSource();
            }
        }
    }
}

I hope it can help you.

我希望它可以帮到你。

#2


1  

You can try to name your TextBoxes unde use the following syntax:

您可以尝试命名您的TextBoxes unde使用以下语法:

 BindingExpression be = tbName.GetBindingExpression(TextBox.TextProperty);
 be.UpdateSource();

 BindingExpression be2 = tbLength.GetBindingExpression(TextBox.TextProperty);
 be2.UpdateSource();

But i would bind to a secondary collection or a copy of an object if i do not want changes to apply immediately, and save the updates to the main list at the user request. When the bounded list is modified again, items changed, removed or added, you may clear the secondary list and do a fresh copy. Or .. if maybe the user wants to revert the changes, clear the bounded one and copy the items from the back.

但是如果我不想立即应用更改,我将绑定到辅助集合或对象的副本,并在用户请求时将更新保存到主列表。当再次修改有界列表,更改,删除或添加项目时,您可以清除次要列表并执行新的副本。或者..如果用户想要还原更改,请清除有界更改并从后面复制项目。

#1


4  

Yes, there is a way, but it is not a very very easy way. First of all you need 2 helper methods:

是的,有办法,但这不是一个非常简单的方法。首先,您需要2个辅助方法:

public static T GetVisualChild<T>(Visual parent) where T : Visual
{
    Visual visual;
    T child = default(T);

    int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
    for (int i = 0; i < childrenCount; i++)
    {
        visual = (Visual)VisualTreeHelper.GetChild(parent, i);
        child = visual as T;
        if (child == null)
        {
            child = GetVisualChild<T>(visual);
        }
        if (child != null)
        {
            break;
        }
    }
    return child;
}

public T FindVisualChild<T>(DependencyObject obj, string name) where T : DependencyObject
{
    DependencyObject child;
    FrameworkElement frameworkElement;
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
    {
        child = VisualTreeHelper.GetChild(obj, i);
        frameworkElement = child as FrameworkElement;
        if (child != null && child is T && frameworkElement != null && frameworkElement.Name == name)
        {
            return (T)child;
        }
        else
        {
            T childOfChild = FindVisualChild<T>(child, name);
            if (childOfChild != null)
            {
                return childOfChild;
            }
        }
    }

    return null;
}

Then you have to give a name to your binded controls. For example "textBox":

然后你必须给你的绑定控件命名。例如“textBox”:

<DataGridTemplateColumn Header="Field">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBox x:Name="textBox" Text="{Binding Path=name, Mode=TwoWay, UpdateSourceTrigger=Explicit}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Length of Field">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBox x:Name="textBox" Text="{Binding Path=length, Mode=TwoWay, UpdateSourceTrigger=Explicit}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

Then in your SaveChanges method, you can use this code:

然后在SaveChanges方法中,您可以使用以下代码:

private void SaveChanges(object sender, RoutedEventArgs e)
{
    DataGridRow dataGridRow;
    DataGridCellsPresenter dataGridCellsPresenter;
    DataGridCell dataGridCell;
    TextBox textBox;
    BindingExpression bindingExpression;

    for (int i = 0; i < Grid1.Items.Count; i++)
    {
        dataGridRow = (DataGridRow)Grid1.ItemContainerGenerator.ContainerFromIndex(i);
        dataGridCellsPresenter = GetVisualChild<DataGridCellsPresenter>(dataGridRow);
        for (int j = 0; j < Grid1.Columns.Count; j++)
        {
            dataGridCell = (DataGridCell)dataGridCellsPresenter.ItemContainerGenerator.ContainerFromIndex(j);
            textBox = FindVisualChild<TextBox>(dataGridCell, "textBox");
            if (textBox != null)
            {
                bindingExpression = BindingOperations.GetBindingExpression(textBox, TextBox.TextProperty);
                bindingExpression.UpdateSource();
            }
        }
    }
}

I hope it can help you.

我希望它可以帮到你。

#2


1  

You can try to name your TextBoxes unde use the following syntax:

您可以尝试命名您的TextBoxes unde使用以下语法:

 BindingExpression be = tbName.GetBindingExpression(TextBox.TextProperty);
 be.UpdateSource();

 BindingExpression be2 = tbLength.GetBindingExpression(TextBox.TextProperty);
 be2.UpdateSource();

But i would bind to a secondary collection or a copy of an object if i do not want changes to apply immediately, and save the updates to the main list at the user request. When the bounded list is modified again, items changed, removed or added, you may clear the secondary list and do a fresh copy. Or .. if maybe the user wants to revert the changes, clear the bounded one and copy the items from the back.

但是如果我不想立即应用更改,我将绑定到辅助集合或对象的副本,并在用户请求时将更新保存到主列表。当再次修改有界列表,更改,删除或添加项目时,您可以清除次要列表并执行新的副本。或者..如果用户想要还原更改,请清除有界更改并从后面复制项目。