当xml文档在WPF中运行时发生更改时,如何刷新xmlDataProvider

本文关键字:何刷新 xmlDataProvider 刷新 文档 xml WPF 运行时 | 更新日期: 2023-09-27 18:15:41

我正在尝试在visual studio, wpf中创建图像查看器/相册创建者。每个专辑的图像路径存储在一个xml文档中,我绑定到该文档中,以便在列表框中显示每个专辑的图像。问题是当我在运行时添加图像或专辑并将其写入xml文档时。我似乎无法更新xml文档的绑定,因此它们也会显示新的图像和相册。在XmlDataProvider上调用Refresh()不会改变任何东西。我不希望重做XmlDataProvider的绑定,只是让它再次从同一源读取。

XAML:

...
<Grid.DataContext>
            <XmlDataProvider x:Name="Images" Source="Data/images.xml" XPath="/albums/album[@name='no album']/image" />
</Grid.DataContext>
...
<Label Grid.Row="1" Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Bottom" Padding="0" Margin="0,0,0,5" Content="{x:Static resx:Resource.AddImageLabel}"/>
<TextBox Grid.Row="1" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Name="newImagePath" Margin="0" />
<Button Grid.Row="1" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Bottom" Name="newImagePathButton" Content="{x:Static resx:Resource.BrowseImageButton}" Click="newImagePathButton_Click" />
...
<ListBox Grid.Column="0" Grid.ColumnSpan="4" Grid.Row="3" HorizontalAlignment="Stretch" Name="thumbnailList" VerticalAlignment="Bottom" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding BindingGroupName=Images}" SelectedIndex="0" Background="#FFE0E0E0" Height="110">
...

背后的代码:

private void newImagePathButton_Click(object sender, RoutedEventArgs e)
{
    string imagePath = newImagePath.Text;
    albumCreator.addImage(imagePath, null);
    //Reset import image elements to default
    newImagePath.Text = "";
    //Refresh thumbnail listbox
    Images.Refresh();
    Console.WriteLine("Image added!");
}
public void addImage(string source, XmlElement parent)
{
    if (parent == null)
    {
        //Use default album
        parent = (XmlElement)root.FirstChild;
    }
    //Create image element with source element within
    XmlElement newImage = xmlDoc.CreateElement(null, "image", null);
    XmlElement newSource = xmlDoc.CreateElement(null, "source", null);
    newSource.InnerText = source;
    newImage.AppendChild(newSource);
    //Add image element to parent
    parent.AppendChild(newImage);
    xmlDoc.Save(xmlFile);

}

非常感谢您的帮助!

当xml文档在WPF中运行时发生更改时,如何刷新xmlDataProvider

在这种情况下,我认为正确的方法是使用ObservableCollection并将其绑定到ListViewItemsSource属性。所以,只要使用对象,不要使用XML文件。

编辑:

整个概念是与Refresh()一起工作。下一个示例是works。检查文档保存后是否呼叫Refresh()

<ListView x:Name="uiList" ItemsSource="{Binding}">
    <ListView.DataContext>
        <XmlDataProvider x:Name="DataSource" Source="c:'XMLFile.xml" XPath="/root/item"  />
    </ListView.DataContext>
    <ListView.ItemTemplate>
        <DataTemplate>
            <Border Width="40" Height="40" Background="Gray">
                <Label Content="{Binding Attributes[0]}" />
            </Border>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

public MainWindow()
{
    InitializeComponent();
    uiList.SelectionChanged += new SelectionChangedEventHandler(uiList_SelectionChanged);
}
void uiList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    string sFile = @"c:'XMLFile.xml";
    XDocument oDoc = XDocument.Load(sFile);
    oDoc.Root.Add(
        new XElement("item", new XAttribute("name", "test3"))
    );
    oDoc.Save(sFile);
    XmlDataProvider oProv = uiList.DataContext as XmlDataProvider;
    oProv.Refresh();
}


可能的问题和解决方案#1

您是否在应用程序中声明了这个XmlDataProvider资源?还有资源阻塞吗?

如果您这样做,XAML UI ListBox元素"thumbnailList"将引用网格面板的XmlDataProvider实例。我猜,因为我看不到你的windows CS文件构造函数中的代码,你指的是XmlDataProvider的应用程序级实例当你地址XmlDataProvider在那里,如在

XmlDataProvider XmlDataProvider = Application.Current.FindResource("Images") as XmlDataProvider

XmlDocument xDoc = xmlDataProvider.Document;

如果是这种情况,请从Grid元素中删除XmlDataProvider资源。现在,当您的后台代码更新XML文件时,UI将自动更新。


可能的问题和解决方案#2

我从你的addmage()方法中看到你引用了一个名为"xDoc"的实例变量。

另一种可能性是您在Window构造函数中创建了一个NEW XmlDocument,而不是引用XAML创建的XmlDocument对象。如果是,获取当前XmlDocument的实例,而不是创建一个新实例。确保声明了资源并从Grid元素

中删除资源声明

XmlDataProvider XmlDataProvider = Application.Current.FindResource("Images") as XmlDataProvider

或者在Grid元素中引用资源(您需要向Grid添加Name),而不在Application中声明该资源。资源块

XmlDataProvider XmlDataProvider = grid.FindResource("Images") as XmlDataProvider;

XmlDocument xDoc = xmlDataProvider.Document;

现在当你的后台代码更新XML文件时,UI将自动更新。


结论

如果在代码中声明这两个类实例变量

XmlDataProvider XmlDataProvider;

XmlDataProvider gridXmlDataProvider;

,并在你的Window构造函数

中添加这段代码

xmlDataProvider = Application.Current.FindResource("Images") as xmlDataProvider;

gridXmlDataProvider = grid.FindResource("Images") as XmlDataProvider;

在添加节点并保存XML文档更改后,在addmage事件处理程序中设置一个停止符。假设您最初从xmlDataProvider加载了oDoc,如上所示。在调试模式下运行并打开Watch窗口,检查xmlDataProvider和gridXmlDataProvider的内容。打开每个文件的Document属性,比较InnerXml属性的内容。在xmlDataProvider(应用程序级别的资源)中,您将找到反映对XML文件的最新节点更改。gridXmlDataProvider (XAML UI元素的资源)则不是这样。InnerXml属性没有显示任何更改。无需更改,无需更新UI

供参考,我有问题#1 -在应用程序中声明的相同XmlDataProvider资源。资源阻塞和在窗口中。资源块。我从后一种声明开始,在通过Application. current . findresource("name")引用XmlDataProvider实例后遇到异常错误,复制并粘贴声明到应用程序中。资源块,将资源保留在窗口中声明。资源块,产生两个引用问题。XAML UI使用Window数据上下文,而我的后台代码使用Application数据上下文更新XML文件!每当我从XML文件中添加或删除节点时,UI (ListBox)都没有得到更新!

BTW XmlDataProvider已经实现了自己的通知机制,不需要使用ObservableCollection。oprove . refresh()不会导致绑定UI的刷新,因为它可能指向XmlDataProvider (Grid元素的)的不同实例,并且就该实例而言,没有发生任何更改。

这个答案对你来说可能太迟了,但我刚刚发现了这些东西,想和你分享一下。

    <XmlDataProvider Source="XMLFile1.xml" XPath="Data"  DataChanged="XmlDataProvider_DataChanged"></XmlDataProvider>
</Window.DataContext>
在cs

  private void XmlDataProvider_DataChanged(object sender, EventArgs e)
        {
            Dispatcher.BeginInvoke((Action)(() =>
            {
                XmlDataProvider oProv = this.DataContext as XmlDataProvider;
                oProv.Refresh();
            }));
        }

取自..http://www.infosysblogs.com/microsoft/2008/03/wpf_updating_xmldataprovider_w.html

        XmlDataProvider xdp = this.Resources["userDataXmlDataProvider1"]  as XmlDataProvider;
        xdp.Source = new Uri(MyPath + @"'Projects.xml");
        FileSystemWatcher watcher = new FileSystemWatcher();
        //set the path of the XML file appropriately as per your requirements
        watcher.Path = MyPath;
        //name of the file i am watching
        watcher.Filter = "Projects.xml";
        //watch for file changed events so that we can refresh the data provider
        watcher.Changed += new FileSystemEventHandler(file_Changed);
        //finally, don't forget to enable watching, else the events won't fire           
        watcher.EnableRaisingEvents = true;

    void file_Changed(object sender, FileSystemEventArgs e)
    {
        XmlDataProvider xdp = this.Resources["userDataXmlDataProvider1"] as XmlDataProvider;
        xdp.Refresh();
    }

and in my UserControl;

    <UserControl.Resources>
    <XmlDataProvider x:Key="userDataXmlDataProvider1" XPath="Projects/Project" IsAsynchronous="True" />
    <CollectionViewSource x:Key="userDataCollectionViewSource1" Source="{StaticResource userDataXmlDataProvider1}"/>
    </UserControl.Resources>
    <Grid DataContext="{StaticResource userDataXmlDataProvider1}">
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="3*"/>
    </Grid.RowDefinitions>
    <ListBox x:Name="listBox1" Grid.Row="1"
             ItemsSource="{Binding}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal" Margin="8,0,8,0">
                    <Label Content="{Binding XPath=ProjectName}" Width="100" Margin="5" />
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>