简体   繁体   English

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

[英]How to refresh xmlDataProvider when xml document changes at runtime in WPF?

I am trying to make a image viewer/album creator in visual studio, wpf. 我试图在WPF的Visual Studio中创建图像查看器/专辑创建者。 The image paths for each album is stored in an xml document which i bind to to show the images from each album in a listbox. 每个专辑的图像路径都存储在一个xml文档中,我绑定该文档以在列表框中显示每个专辑的图像。 The problem is when i add a image or an album at runtime and write it to the xml document. 问题是当我在运行时添加图像或相册并将其写入xml文档时。 I can't seem to make the bindings to the xml document update so they show the new images and albums aswell. 我似乎无法更新xml文档的绑定,因此它们也显示新的图像和相册。 Calling Refresh() on the XmlDataProvider doesn't change anything. 在XmlDataProvider上调用Refresh()不会更改任何内容。 I don't wish to redo the binding of the XmlDataProvider, just make it read from the same source again. 我不想重做XmlDataProvider的绑定,只需再次从相同的源读取它即可。

XAML: 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">
...

Code behind: 后面的代码:

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);

} }

Thank you very much for any help! 非常感谢您的帮助!

The right way in this situation I beleive is to use ObservableCollection and bind it to ItemsSource property of your ListView . 在这种情况下,我相信正确的方法是使用ObservableCollection并将其绑定到ListView ItemsSource属性。 So, just play with objects and no tricks with XML files. 因此,只需要玩对象,而不要玩XML文件。

Edit: 编辑:

Entire concept is work with Refresh() . 整个概念是使用Refresh() Next sample is works. 下一个样本是可行的。 Check if Refresh() call is made after document saving. 保存文档后检查是否进行了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();
}

Possible Problem and Solution #1 可能的问题和解决方案#1

Do you have this XmlDataProvider resource declared in the Application.Resources block as well? 您是否还在Application.Resources块中声明了XmlDataProvider资源?

If you do, the XAML UI ListBox element "thumbnailList" refers to the Grid panel's instance of the XmlDataProvider. 如果这样做,则XAML UI ListBox元素“ thumbnailList”是指XmlDataProvider的“网格”面板的实例。 I'm guessing, since I can't see the code in your Window CS file constructor, that you refer to the Application-level instance of the XmlDataProvider when you address the XmlDataProvider there as in 我猜,因为我看不到您的Window CS文件构造函数中的代码,所以当您在其中寻址XmlDataProvider时,您引用的是XmlDataProvider的应用程序级实例。

XmlDataProvider xmlDataProvider = Application.Current.FindResource("Images") as XmlDataProvider; XmlDataProvider xmlDataProvider = Application.Current.FindResource(“ Images”)作为XmlDataProvider;

XmlDocument xDoc = xmlDataProvider.Document; XmlDocument xDoc = xmlDataProvider.Document;

If this is the case, remove the XmlDataProvider resource from the Grid element. 如果是这种情况,请从Grid元素中删除XmlDataProvider资源。 Now when your code-behind updates the XML file the UI will automatically update. 现在,当您的代码隐藏更新XML文件时,UI将自动更新。


Possible Problem and Solution #2 可能的问题和解决方案2

I see from your addImage() method that you refer to an instance variable named "xDoc". 我从您的addImage()方法中看到,您引用了一个名为“ xDoc”的实例变量。

The other possibility is that you are creating a NEW XmlDocument in your Window constructor, instead of referencing the XAML created XmlDocument object. 另一种可能性是您在Window构造函数中创建一个NEW XmlDocument,而不是引用XAML创建的XmlDocument对象。 If so, get the instance of the current XmlDocument instead of creating a new instance. 如果是这样,请获取当前XmlDocument的实例,而不是创建一个新实例。 Make sure to declare the resource at the Application level and remove the resource declaration from the Grid element 确保在应用程序级别声明资源,并从Grid元素中删除资源声明

XmlDataProvider xmlDataProvider = Application.Current.FindResource("Images") as XmlDataProvider; XmlDataProvider xmlDataProvider = Application.Current.FindResource(“ Images”)作为XmlDataProvider;

Or reference the resource at the Grid element (you will need to add a Name to the Grid) and do not declare the resource in the Application.Resources block 或在Grid元素处引用资源(您将需要在Grid中添加一个Name),并且不要在Application.Resources块中声明资源。

XmlDataProvider xmlDataProvider = grid.FindResource("Images") as XmlDataProvider; XmlDataProvider xmlDataProvider = grid.FindResource(“ Images”)as XmlDataProvider;

XmlDocument xDoc = xmlDataProvider.Document; XmlDocument xDoc = xmlDataProvider.Document;

Now when your code-behind updates the XML file the UI will automatically update. 现在,当您的代码隐藏更新XML文件时,UI将自动更新。


Conclusions 结论

If you declare these two class instance variables in your code-behind 如果您在后台代码中声明了这两个类实例变量

XmlDataProvider xmlDataProvider; XmlDataProvider xmlDataProvider;

XmlDataProvider gridXmlDataProvider; XmlDataProvider gridXmlDataProvider;

and have this code in your Window constructor 并在您的Window构造函数中包含此代码

xmlDataProvider = Application.Current.FindResource("Images") as XmlDataProvider; xmlDataProvider = Application.Current.FindResource(“ Images”)作为XmlDataProvider;

gridXmlDataProvider = grid.FindResource("Images") as XmlDataProvider; gridXmlDataProvider = grid.FindResource(“ Images”)作为XmlDataProvider;

Put a stop in the addImage event handler right you add a Node and save the XML Document changes. 在添加节点并保存XML文档更改的位置,在addImage事件处理程序中停下来。 Assuming you originally loaded oDoc from xmlDataProvider as shown above. 假设您最初是从xmlDataProvider加载了oDoc,如上所示。 Run in Debug Mode and open a Watch window and inspect the contents of xmlDataProvider and gridXmlDataProvider. 在“调试模式”下运行,然后打开“监视”窗口并检查xmlDataProvider和gridXmlDataProvider的内容。 Open the Document property on each, and compare the contents of the InnerXml property. 打开每个文档属性,然后比较InnerXml属性的内容。 On the xmlDataProvider (the Application-level's resource) you will find the latest node changes to the XML file are reflected. 在xmlDataProvider(应用程序级的资源)上,您会发现反映了XML文件的最新节点更改。 Not so on the gridXmlDataProvider (the XAML UI element's resource). 在gridXmlDataProvider(XAML UI元素的资源)上不是这样。 InnerXml property shows no changes. InnerXml属性未显示任何更改。 No changes, not need to update the UI. 无需更改,无需更新UI。

FYI I had Problem #1 - the same XmlDataProvider resource declared in the Application.Resources block AND in the Window.Resources block. 仅供参考,我有问题#1-在Application.Resources块和Window.Resources块中声明的XmlDataProvider资源相同。 I started out with the latter declaration, ran into an exception error after I referred to the XmlDataProvider instance via Application.Current.FindResource("name"), copy and pasted the declaration into the Application.Resources block, LEAVING the resource declared in the Window.Resources block, creating a TWO REFERENCE problem. 我从后面的声明开始,在通过Application.Current.FindResource(“ name”)引用XmlDataProvider实例后,遇到异常错误,将声明复制并粘贴到Application.Resources块中,从而保留了Window.Resources块,创建两个引用问题。 The XAML UI used the Window data context, while my code-behind updated the XML file with the Application data context! XAML UI使用Window数据上下文,而我的代码背后则使用Application数据上下文更新了XML文件! Whenever I added or removed nodes from the XML file the UI (ListBox) did not get updated! 每当我从XML文件添加或删除节点时,UI(列表框)都不会更新!

BTW XmlDataProvider already implements its own notification mechanism, no need use an ObservableCollection. BTW XmlDataProvider已经实现了自己的通知机制,无需使用ObservableCollection。 oProv.Refresh() does not cause the refresh of the bound UI because it may point to a different instance of the XmlDataProvider (the Grid element's), and as far as that instance is concerned, no changes have happened. oProv.Refresh()不会刷新绑定的UI,因为它可能指向XmlDataProvider的另一个实例(Grid元素的实例),就该实例而言,没有发生任何更改。

This answer probably comes too late for you, but I just found this stuff out, thought I share it. 对于您来说,这个答案可能来不及了,但是我发现了这些东西,以为我可以分享。

in xml 在xml中

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

in cs 在cs中

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

Taken from.. http://www.infosysblogs.com/microsoft/2008/03/wpf_updating_xmldataprovider_w.html 摘自:http://www.infosysblogs.com/microsoft/2008/03/wpf_updating_xmldataprovider_w.html

Ive used below; 我在下面用过;

        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;

and

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

    }

and in my UserControl; 并在我的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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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