Как обновить xmlDataProvider при изменении документа xml во время выполнения в WPF? - PullRequest
2 голосов
/ 23 ноября 2011

Я пытаюсь создать средство просмотра изображений / создания альбомов в visual studio, wpf. Пути к изображениям для каждого альбома хранятся в XML-документе, к которому я привязываюсь, чтобы отображать изображения из каждого альбома в списке. Проблема заключается в том, когда я добавляю изображение или альбом во время выполнения и записываю его в документ XML. Я не могу сделать привязки к обновлению документа XML, чтобы они также отображали новые изображения и альбомы. Вызов Refresh() на XmlDataProvider ничего не меняет. Я не хочу переделывать привязку 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);

}

Большое спасибо за любую помощь!

Ответы [ 4 ]

4 голосов
/ 23 ноября 2011

В этой ситуации я верю, что нужно использовать ObservableCollection и связать его с ItemsSource свойством ListView. Так что просто играйте с объектами, а не трюки с XML-файлами.

Edit:

Вся концепция работы с 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 голос
/ 26 октября 2012

Возможная проблема и решение # 1

Есть ли у вас этот ресурс XmlDataProvider, также объявленный в блоке Application.Resources?

Если вы это сделаете,элемент ListBox пользовательского интерфейса XAML «thumbnailList» относится к экземпляру панели «Сетка» XmlDataProvider.Я предполагаю, что поскольку я не вижу кода в конструкторе файлов Window CS, вы обращаетесь к экземпляру уровня приложения XmlDataProvider при обращении к нему XmlDataProvider, как в

XmlDataProvider xmlDataProvider = Application.Current.FindResource ("Images") как XmlDataProvider;

XmlDocument xDoc = xmlDataProvider.Document;

В этом случае удалите ресурс XmlDataProvider из элемента Grid.Теперь, когда ваш программный код обновляет XML-файл, пользовательский интерфейс будет автоматически обновляться.


Возможная проблема и решение # 2

Я вижу из вашего метода addImage (), что вы ссылаетесь напеременная экземпляра с именем "xDoc".

Другая возможность состоит в том, что вы создаете NEW XmlDocument в своем конструкторе Window вместо ссылки на объект XmlDocument, созданный в XAML.Если это так, получите экземпляр текущего XmlDocument вместо создания нового экземпляра.Обязательно объявите ресурс на уровне приложения и удалите объявление ресурса из элемента Grid

XmlDataProvider xmlDataProvider = Application.Current.FindResource ("Images") как XmlDataProvider;

Или укажите ссылку наресурс в элементе Grid (вам необходимо добавить имя в Grid) и не объявляйте ресурс в блоке Application.Resources

XmlDataProvider xmlDataProvider = grid.FindResource ("Images") как XmlDataProvider;

XmlDocument xDoc = xmlDataProvider.Document;

Теперь, когда ваш программный код обновляет файл XML, пользовательский интерфейс будет автоматически обновляться.


Выводы

Если вы объявите эти две переменные экземпляра класса в своем коде позади

XmlDataProvider xmlDataProvider;

XmlDataProvider gridXmlDataProvider;

и получите этот код в конструкторе Window

xmlDataProvider = Application.Current.FindResource ("Images") как XmlDataProvider;

gridXmlDataProvider = grid.FindResource ("Images") как XmlDataProvider;

Поставьте точку в обработчике события addImage сразу после добавления узла и сохраните изменения документа XML.Предполагая, что вы изначально загрузили oDoc из xmlDataProvider, как показано выше.Запустите в режиме отладки, откройте окно Watch и просмотрите содержимое xmlDataProvider и gridXmlDataProvider.Откройте свойство Document для каждого и сравните содержимое свойства InnerXml.На xmlDataProvider (ресурс уровня приложения) вы найдете последние изменения узла в файле XML.Не так в gridXmlDataProvider (ресурс элемента XAML UI).Свойство InnerXml не показывает изменений.Без изменений, не нужно обновлять интерфейс.

К вашему сведению У меня была проблема # 1 - тот же ресурс XmlDataProvider, объявленный в блоке Application.Resources И в блоке Window.Resources.Я начал с последнего объявления, столкнулся с ошибкой исключения после того, как обратился к экземпляру XmlDataProvider через Application.Current.FindResource ("name"), скопировал и вставил объявление в блок Application.Resources, оставив ресурс, объявленный вБлок Window.Resources, создающий ДВА РЕФЕРЕНЦИИ.Пользовательский интерфейс XAML использовал контекст данных Window, а мой программный код обновил XML-файл контекстом данных Application!Всякий раз, когда я добавлял или удалял узлы из файла XML, пользовательский интерфейс (ListBox) не обновлялся!

КСТАТИ XmlDataProvider уже реализует свой собственный механизм уведомлений, нет необходимости использовать ObservableCollection.oProv.Refresh () не вызывает обновления связанного пользовательского интерфейса, поскольку он может указывать на другой экземпляр XmlDataProvider (элемента Grid), и, что касается этого экземпляра, никаких изменений не произошло.

Этот ответ, вероятно, приходит слишком поздно для вас, но я только что нашел этот материал, подумал, что поделюсь им.

0 голосов
/ 05 марта 2016

Взято из .. 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>
0 голосов
/ 14 февраля 2014

в 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();
            }));
        }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...