Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > WPF и Silverlight > Создание ссылки из кода в XAML


Автор: DmitriyS 9.7.2011, 18:48
Имем: некоторое окно с DataGrid или ListView (не особо важно). При двойном щелчке на одной из строк, вызывается редактор содержимого этой строки, т.е. открывается диалог для правки данных(на это есть причины). При этом, в основном окне, имеем ObjectDataProvider src из которого поступают данные в DataGrid(ListView).
Требуется: Передать src в конструктор редактора содержимого, ибо src настроен определенным образом и делать его клоны или копии - не желательно. Таким образом передать ссылку на данные в окно редактора до привязки данных к визуальным элементам самого редактора.

XAML основного окна (укорочено)
Код

<Window.Resources>
        <ObjectDataProvider x:Key="resMatDataProvider" ObjectType="{x:Type l:MaterialDataProvider}"/>
        <ObjectDataProvider x:Key="resMaterialData" ObjectInstance="{StaticResource resMatDataProvider}" MethodName="GetMaterials"/>
</Window.Resources>
<wpf:DataGrid  Name="table" CanUserAddRows="False"
                CanUserDeleteRows="False"
                CanUserReorderColumns="False"
                CanUserResizeRows="False"
                AutoGenerateColumns="False"
                IsReadOnly="True"
                MouseDoubleClick="table_OnEdit"
                ItemsSource="{Binding Source={StaticResource resMaterialData}}"/>

Обработчик события основного окна
Код

private void table_OnEdit(object sender, MouseButtonEventArgs e)
{
            MaterialEdit ed = new MaterialEdit(provider);
            ed.DataContext = table.SelectedItem;
            ed.Owner = this;
            ed.ShowDialog();
}


XAML редактора
Код

<Window.Resources>
            <!-- Это делать нельзя -->
            <!-- ObjectDataProvider x:Key="resDataProvider" ObjectType="{x:Type lx:MaterialDataProvider}"/-->

            <! -- Это нужно, ибо в resDataProvider имеется починенные данные -->
            <ObjectDataProvider x:Key="resMatWidthData" ObjectInstance="{StaticResource resDataProvider}" MethodName="GetMatWidths" />
</Window.Resources>
<!-- Далее, поэлементный биндинг с выборкой подчиненных данных -->


Этот код не работает!!
Код

public MaterialEdit(MaterialDataProvider provider)
{
    this.provider = provider;
    Resources.Add("resDataProvider", provider);
    // следующая функция, зачемто, вызывает Resources.Clear()
    InitializeComponent();
}

Автор: DmitriyS 9.7.2011, 20:05
Возможно, я чтото не так написал?
 smile 

Автор: Kefir 11.7.2011, 10:17
Так создай проперти в твоем MaterialEdit, куда будешь запихивать нужный объект.
Код

MaterialEdit ed = new blaa();
ed.MyVeryImportantObjectInstance = FindResource("resMatDataProvider");

Автор: DmitriyS 11.7.2011, 20:45
Нет Kerif, такой фокус не прокатит. Ибо resMatDataProvider используется в привязке (Binding), где нужены объекты source и target.
В моем случае из-за отсутствия, можно сказать, интерфейса связываения source, прикручиваться не к чему.
Получается привязка null=>target. До того как привязать данные, загрузчик XAML проверяет наличие источника и уведомляет об отсутствии оного возбуждением исключения.(Это если скомпилируется)

Например:
Код

<Window.Resource>
  <l:MyDataClass x:Key="MyData">
<!-- здесь создается новый экземпляр MyDataClass и помещается в словарь ресурсов окна  и ключ оного в данном случае MyData -->
</Window.Resource>
<!-- где-то в разметке -->
  <TextBlock Text="{Binding Source={StaticResource MyData}, Path=SomeProperty}" ... />

Попробуй теперь удалить <l:MyDataClass ...>
... верно. JIT компилятор выдаст ошибку. Не скомпилируется. Не соберется. Приложения не будет.
Что будеш делать если: экземпляр класса MyDataClass уже существует в коде и должен быть источником данных для двух или более окон но не должен быть статическим?

Я-бы мог создать класс-обертку, заключающий в себе resMatDataProvider и DataRowView и передать его редактору в DataContext.
Но это опять таки не прокатит.

Тем не менее. Вопрос в том, что-бы в ресурс XAML запихнуть данные из кода до его инициализации.
Но при загрузке и инициализации XAML словарь ресурсов окна сбрасывается, что в моем случае совершенно не уместно.

Автор: Kefir 12.7.2011, 09:00
Я честно не занимался такими экспериментами как ты, но сделай так:

Код

<Window.Resources>
  <!-- Ставим в ресурсы абсолютно пустой ObjectDataProvider -->
  <ObjectDataProvider x:Key="odp" ObjectInstance="{x:Null}"/>
</Window.Resources>


Код окна:
Код

public MyWindow(MyObjectCls myObject)
{
  InitializeComponent();
  
  // Находим наш провайдер и указываем ему на правильный инстанс
  var odp = (ObjectDataProvider)this.FindResource("odp");
  odp.ObjectInstance = myObject;
}



Всё должно прекрасно обновиться (и байндинги тоже). Я, конечно, не очень понимаю зачем в этом случае нужен ObjectDataProvider, но наверное есть на то свои причины (возможно ты просто выкладываешь упрощенный код своей системы).

Удачи. Приходи назад, если шо.

Автор: DmitriyS 12.7.2011, 20:50
Цитата

Всё должно прекрасно обновиться (и байндинги тоже). Я, конечно, не очень понимаю зачем в этом случае нужен ObjectDataProvider, но наверное есть на то свои причины (возможно ты просто выкладываешь упрощенный код своей системы).


Да. Спасибо добрый человек. Заработало.
 Если есть идеи как не использовать ObjectDataProvider. С радостью рассмотю предложения.

Основное окно
Код

<wpf:DataGridTextColumn Header="Толщина" Width="100" Binding="{Binding Path=mat_width_id,
                                            Converter={StaticResource MatWidthConverter},
                                            ConverterParameter={StaticResource resMatDataProvider}}"/>


Окно редактора
Код

<ComboBox Grid.Column="1" Grid.Row="1" Height="23" Margin="12,6,12,0" Name="cMatWidth" VerticalAlignment="Top"
                  ItemsSource="{Binding Source={StaticResource resMatWidthData}}"
                  SelectedItem="{Binding
                                    Path=[mat_width_id],
                                    Converter={StaticResource MatWidthConverter},
                                    ConverterParameter={StaticResource resDataProvider}}">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Path=width}"/>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

Автор: DmitriyS 12.7.2011, 22:02
Через несколько минут упорного умстенного труда.
Код

<Window.Resource>
<!-- то, что раньше было написано -->
    <ObjectDataProvider x:Key="resMatNameFilter" ObjectInstance="{StaticResource resMatDataProvider}" MethodName="MatNameFilter">
        <ObjectDataProvider.MethodParameters>
            <ObjectDataProvider.MethodParameters>
                <x:NullExtension/>
            </ObjectDataProvider.MethodParameters>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</Window.Resource>
<!-- ... ну и привязка -->
    <Grid Grid.Row="0">
        <StackPanel Orientation="Horizontal" Margin="0,0,6,0" HorizontalAlignment="Left">
            <TextBlock Text="Фильтр по наименованиям" Margin="6"/>
            <TextBox Name="txtFilter" Margin="6" Width="50"
                    Text="{Binding Source={StaticResource resMatNameFilter},
                            Path=MethodParameters[0],
                            BindsDirectlyToSource=True,
                            UpdateSourceTrigger=PropertyChanged}"/>
        </StackPanel>
    </Grid>
    <wpf:DataGrid  Name="table" Margin="6" Grid.Row="1"
                   CanUserAddRows="False"
                   CanUserDeleteRows="False"
                   CanUserReorderColumns="False"
                   CanUserResizeRows="False"
                   AutoGenerateColumns="False"
                   IsReadOnly="True"
                   MouseDoubleClick="table_OnEdit"
                   ItemsSource="{Binding Source={StaticResource resMaterialData}}">
        <wpf:DataGrid.Columns>
            <wpf:DataGridTextColumn Header="Наименование" Width="150" Binding="{Binding Path=name}"/>
            <wpf:DataGridTextColumn Header="Толщина" Width="100" Binding="{Binding Path=mat_width_id,
                                        Converter={StaticResource MatWidthConverter},
                                        ConverterParameter={StaticResource resMatDataProvider}}"/>
    </wpf:DataGrid>


Я так думаю, без ObjectDataProvider это было бы, мягко говоря, затруднительно.
Или мне это только кажется?

Еще раз спасибо за совет. Теперь можно "воевать" по взрослому, потому как это, лиш мелкая таблица БД из большого програмного комплекса

Автор: Kefir 13.7.2011, 10:51
Да, тут в твоем случае особо ничего не сделаешь. Параметры конвертеров нельзя биндить. Поэтому ничего лучше чем ObjectDataProvider в голову не идет. Удачи!

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)