簡體   English   中英

在WPF中運行時xml文檔更改時,如何刷新xmlDataProvider?

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

我試圖在WPF的Visual Studio中創建圖像查看器/專輯創建者。 每個專輯的圖像路徑都存儲在一個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);

}

非常感謝您的幫助!

在這種情況下,我相信正確的方法是使用ObservableCollection並將其綁定到ListView ItemsSource屬性。 因此,只需要玩對象,而不要玩XML文件。

編輯:

整個概念是使用Refresh() 下一個樣本是可行的。 保存文檔后檢查是否進行了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

您是否還在Application.Resources塊中聲明了XmlDataProvider資源?

如果這樣做,則XAML UI ListBox元素“ thumbnailList”是指XmlDataProvider的“網格”面板的實例。 我猜,因為我看不到您的Window CS文件構造函數中的代碼,所以當您在其中尋址XmlDataProvider時,您引用的是XmlDataProvider的應用程序級實例。

XmlDataProvider xmlDataProvider = Application.Current.FindResource(“ Images”)作為XmlDataProvider;

XmlDocument xDoc = xmlDataProvider.Document;

如果是這種情況,請從Grid元素中刪除XmlDataProvider資源。 現在,當您的代碼隱藏更新XML文件時,UI將自動更新。


可能的問題和解決方案2

我從您的addImage()方法中看到,您引用了一個名為“ xDoc”的實例變量。

另一種可能性是您在Window構造函數中創建一個NEW XmlDocument,而不是引用XAML創建的XmlDocument對象。 如果是這樣,請獲取當前XmlDocument的實例,而不是創建一個新實例。 確保在應用程序級別聲明資源,並從Grid元素中刪除資源聲明

XmlDataProvider xmlDataProvider = Application.Current.FindResource(“ Images”)作為XmlDataProvider;

或在Grid元素處引用資源(您將需要在Grid中添加一個Name),並且不要在Application.Resources塊中聲明資源。

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

XmlDocument xDoc = xmlDataProvider.Document;

現在,當您的代碼隱藏更新XML文件時,UI將自動更新。


結論

如果您在后台代碼中聲明了這兩個類實例變量

XmlDataProvider xmlDataProvider;

XmlDataProvider gridXmlDataProvider;

並在您的Window構造函數中包含此代碼

xmlDataProvider = Application.Current.FindResource(“ Images”)作為XmlDataProvider;

gridXmlDataProvider = grid.FindResource(“ Images”)作為XmlDataProvider;

在添加節點並保存XML文檔更改的位置,在addImage事件處理程序中停下來。 假設您最初是從xmlDataProvider加載了oDoc,如上所示。 在“調試模式”下運行,然后打開“監視”窗口並檢查xmlDataProvider和gridXmlDataProvider的內容。 打開每個文檔屬性,然后比較InnerXml屬性的內容。 在xmlDataProvider(應用程序級的資源)上,您會發現反映了XML文件的最新節點更改。 在gridXmlDataProvider(XAML UI元素的資源)上不是這樣。 InnerXml屬性未顯示任何更改。 無需更改,無需更新UI。

僅供參考,我有問題#1-在Application.Resources塊和Window.Resources塊中聲明的XmlDataProvider資源相同。 我從后面的聲明開始,在通過Application.Current.FindResource(“ name”)引用XmlDataProvider實例后,遇到異常錯誤,將聲明復制並粘貼到Application.Resources塊中,從而保留了Window.Resources塊,創建兩個引用問題。 XAML UI使用Window數據上下文,而我的代碼背后則使用Application數據上下文更新了XML文件! 每當我從XML文件添加或刪除節點時,UI(列表框)都不會更新!

BTW XmlDataProvider已經實現了自己的通知機制,無需使用ObservableCollection。 oProv.Refresh()不會刷新綁定的UI,因為它可能指向XmlDataProvider的另一個實例(Grid元素的實例),就該實例而言,沒有發生任何更改。

對於您來說,這個答案可能來不及了,但是我發現了這些東西,以為我可以分享。

在xml中

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

    }

並在我的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