Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / WPF, Silverlight Новый топик    Ответить
 DataTemplate и ViewModel  [new]
Qwe.Qwe1
Member

Откуда:
Сообщений: 232
Я для окна (View) установил контекст моей VM, в которой есть объект MyObj определенного класса. Биндинг ко всем контролам окна я делаю так: MyObj.Name, MyObj.SubName и т.д. Так же для этой View есть несколько DataTemplate, которые выбираются в зависимости от выбранного элемента комбобокса:
<ContentPresenter Content="{Binding SelectedCmbBoxID}" ContentTemplateSelector="{StaticResource MyObjTemplateSelector}"/>

В самих DataTemplat'ах биндинг тоже указываю аналогичным образом: MyObj.DocDate, MyObj.DocNum и т.д.
<DataTemplate x:Key="DocTemplate" DataType="{x:Type local:MyObject}">
    <Grid>
       <...>
       <TextBox Grid.Row="1" Text="{Binding MyObj.DocDate}"/>
       <TextBox Grid.Row="2" Text="{Binding MyObj.DocNum }"/>
       <...>


1) Но биндинг в DataTemplate не работает - как указать ему VM? Идеально не в каждом биндигне писать конструкция, типа поиск контекста окна и т.д., а как-то один раз.
2) Если мне понадобится эти шаблоны данных применить в другой View, с другой VM, где будет объект того же класса MyObject, но называться, к примеру, SelectedObj - как выйти из положения, что я уже указал имя объекта MyObj в шаблоне? Может можно как-то указать VM и имя объекта?
17 фев 19, 12:24    [21812018]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 19953
Честно говоря, очень путано объяснил, я мало что понял.

С одной стороны, ты говоришь, что в модели окна есть объект MyObj (свойство этого типа, надо полагать)
С другой стороны, ты говоришь, что для окна существует несколько шаблонов ("для окна (View).... для этой View"), тогда где находится комбобокс, и почему в приведенном примере шаблона DataType=MyObject, а не модель окна?

Если же у тебя DocDate и DocNum - это свойства типа local:MyObject, то зачем ты пишешь в биндинге MyObj? У тебя же в контексте уже MyObject, следовательно писать надо просто
       <TextBox Grid.Row="1" Text="{Binding DocDate}"/>
       <TextBox Grid.Row="2" Text="{Binding DocNum }"/>
в общем, для лучшего понимания надо более развернутый пример
17 фев 19, 12:38    [21812028]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Qwe.Qwe1
Member

Откуда:
Сообщений: 232
Попробую немного расширить пример.
Вот модель данных:
public class MyData
{
    public string Name { get; set; }
    public string Address { get; set; }
    public string DocDate { get; set; }
    public string DocNum { get; set; }
    <...>
}

Вот ViemModel:
public class MyViewModel : INotifyPropertyChanged
{
    public MyData MyObject { get; set; }
    <...>
}

Устанавливаем для окна ViewModel:
public MyWindow(MyViewModel vm)
{
    InitializeComponent();
    DataContext = vm;
}

Вот так в окне я делаю биндинг:
<Window x:Class="MyProject.MyWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:local="clr-namespace:MyProject"/>
        <...>
    <Grid>
        <...>
        <TextBox Grid.Row="1" Text="{Binding MyObject.Name}"/>
        <TextBox Grid.Row="2" Text="{Binding MyObject.Address}"/>
        <...>

А шаблон данных для меня - это как бы "вырезанная часть" такого грида с контролами для включения при выборе в комбобоксе:
<DataTemplate x:Key="DocTemplate" DataType="{x:Type local:MyData}">
    <Grid>
       <...>
       <TextBox Grid.Row="1" Text="{Binding MyObject.DocDate}"/>
       <TextBox Grid.Row="2" Text="{Binding MyObject.DocNum }"/>
       <...>
17 фев 19, 12:57    [21812035]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 19953
Как я уже сказал выше, если у тебя DataTemplate нацелен на тип MyData, у него никак не может быть в контексте свойства MyObject, к которому ты пытаешься обратиться, потому что MyObject - это свойство MyViewModel, а в шаблон "знает" только про свойства MyData и обращаться к ним нужно напрямую
17 фев 19, 13:02    [21812037]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 19953
Qwe.Qwe1
2) Если мне понадобится эти шаблоны данных применить в другой View, с другой VM, где будет объект того же класса MyObject, но называться, к примеру, SelectedObj - как выйти из положения, что я уже указал имя объекта MyObj в шаблоне? Может можно как-то указать VM и имя объекта?
Соответственно, этот вопрос перестает иметь смысл, так как шаблон относится только к типу MyData, независимо от того, в каком свойстве какого объекта он хранится
17 фев 19, 13:04    [21812039]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Qwe.Qwe1
Member

Откуда:
Сообщений: 232
Логично. Убрал MyObject из шаблона, но студия пишет BindingExpression path error. Откуда в шаблон будут подставляться данные?
17 фев 19, 13:16    [21812043]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 19953
Qwe.Qwe1
Откуда в шаблон будут подставляться данные?

<ContentPresenter Content="{Binding MyObject}" ContentTemplate="{StaticResource DocTemplate}" />
17 фев 19, 13:24    [21812049]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Qwe.Qwe1
Member

Откуда:
Сообщений: 232
Так заработало, единственный момент: в шаблоне данных, при таком подходе, нет доступа к ВьюМодели. А у меня в ней есть свойство
public bool IsEditMode { get; set; }

которое определяет стиль всех Тексбоксов: ReadOnly или нет (в зависимости от того, нажал ли пользователь кнопку Редактировать в окне). Чтобы доступ ко вьюмодели остался я делал так:
<DataTemplate x:Key="DocTemplate" DataType="{x:Type local:MyData}">
    <ScrollViewer Style="{StaticResource ScrollViewerStyle}">
        <Grid>
        <...>


<Style x:Key="ScrollViewerStyle" TargetType="ScrollViewer">
    <Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
    <Setter Property="DataContext" Value="{Binding DataContext, RelativeSource={RelativeSource AncestorType=Window}}"/>
</Style>

Тогда надо оставить в биндинге в шаблоне все как было (MyObject.DocDate и т.д.). Но тогда идет завязка на имя объекта MyObject (п. 2 вопроса).
17 фев 19, 13:42    [21812052]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 19953
Логично сделать свойство IsEditMode внутри MyData
17 фев 19, 13:46    [21812053]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 19953
Тут ты должен мыслить в подходах обычной инкапсуляции - ты выделяешь класс MyData, который будет использоваться в разных контекстах. Родитель зависит от MyData, но MyData никак не должен зависеть от родителя, иначе начинается спагетти-код.
17 фев 19, 13:50    [21812057]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Qwe.Qwe1
Member

Откуда:
Сообщений: 232
Подумаю, спасибо.
Правда свойства типа IsEditMode, с моей точки зрения, чисто ВьюМодельные и в данные MyData, которые я считываю из БД, они не очень подходят... Этот момент пока не знаю как обыграть.
17 фев 19, 13:55    [21812061]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 19953
Если ты выделяешь отдельный шаблон для MyData, то MyData автоматически становится вьюмоделью. Если ты хочешь еще сильнее разделить слои, то делай еще одну вьюмодель, в которой MyData будет всего лишь свойством. Но большого смысла в этом я не вижу
17 фев 19, 13:58    [21812063]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 19953
Qwe.Qwe1
Правда свойства типа IsEditMode, с моей точки зрения, чисто ВьюМодельные
ну как сказать. Например можно запретить сохранение модели в базу при IsEditMode=false, так сказать, страхуясь от ошибки программиста при дальнейшем рефакторинге.
17 фев 19, 14:00    [21812064]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Qwe.Qwe1
Member

Откуда:
Сообщений: 232
А нельзя сделать как-то так? Тогда данные в шаблоне не будут знать о VM, но хотя бы так можно было бы задать ReadOnly?
<ContentPresenter Content="{Binding MyObject}" ContentTemplate="{StaticResource DocTemplate}" />
    <ContentPresenter.Resources>
        <Style TargetType="TextBox">
            <Setter Property="IsReadOnly" Value="True"/>
        </Style>
    </ContentPresenter.Resources>
</ContentPresenter>
18 фев 19, 10:31    [21812582]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Супер_Пав
Member

Откуда: Москва
Сообщений: 327
Я бы сделал новую ВьюМодель:
class MyDataViewModel : INotifyPropertyChanged
{
     public MyData Model {get;}
     public MyDataViewModel (MyData model)
     {
           Model = model;
     }
     public string Name 
     {
          get => Model.Name;
          set
          {
               if (Model.Name != value)
               {
                     Model.Name = value;
                     OnPropertyChange("Name");
               }
          }
     }
     // Далее описываем все св-ва по аналогии с Name
     // И добавляем IsEditMode 
}
18 фев 19, 11:35    [21812719]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 19953
Qwe.Qwe1
А нельзя сделать как-то так? Тогда данные в шаблоне не будут знать о VM, но хотя бы так можно было бы задать ReadOnly?
Это, в общем, плохая затея, потому что если захочется потом на какой-то текстбокс функционал навесить (какой-нить подкрашивающий стиль), стиль по умолчанию работать перестанет. А так как ты редактируешь шаблон для MyData, ты не знаешь (как бы), что тебе навязывается какой-то функционал извне.

Это особенно проявляется, когда проект большой или над ним работают несколько разрабов.
18 фев 19, 11:39    [21812726]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Qwe.Qwe1
Member

Откуда:
Сообщений: 232
Супер_Пав
Может быть своя ВьюМодель и неплохая идея, только вот перечислять все поля в ней не очень хочется.

Shocker.Pro
А у меня так и не получилось переопределить стиль. Не в ресурсах ContentPresenter'а, не если обернуть его StackPanel'ю и для не задать ресурсы со стилями Текстбокса.

У меня другой вопрос: раньше я ID выбранного в комобосоке элемента использовал для DataSelector'а, теперь у меня используется MyObject:
<ContentPresenter Content="{Binding MyObject}" ContentTemplate="{StaticResource DocTemplate}"/>


Я делаю так и при смене элемента комбобокса контент (шаблон) не меняется. То есть при открытии окна шаблон выбирается, а при смене в комобоксе значений - уже нет. Почему?
<ComboBox ItemsSource="{x:Static MyProject.MyDic}" DisplayMemberPath="Name" SelectedValue="{Binding SelectedDocTypeID}" SelectedValuePath="ID"/>

Во вью-модели:
public int? SelectedDocTypeID
{
    get { return MyObject.DocTypeID; }
    set
    {
        MyObject.DocTypeID = value;
        OnPropertyChanged();
        OnPropertyChanged("MyObject");
    }
}
20 фев 19, 12:56    [21815321]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 19953
Qwe.Qwe1
Может быть своя ВьюМодель и неплохая идея, только вот перечислять все поля в ней не очень хочется.
А зачем? У тебя один фиг представление зависит сейчас от MyData. Так что ты не ухудшишь ситуацию, если новая модель будет содержать свойства типа IsReadOnly и целиком объект MyData в каком-то поле. Вряд ли тебе нужны для него OnPropertyChanged
20 фев 19, 13:16    [21815340]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 19953
Qwe.Qwe1
У меня другой вопрос: раньше я ID выбранного в комобосоке элемента использовал для DataSelector'а, теперь у меня используется MyObject:
Селектор шаблона для конкретного элемента имеет на входе сам шаблонизируемый объект и только. То есть шаблон выбирается в зависимости от состояния объекта. Таким образом, тебе выбранный в комбобоксе элемент нужно привязать на некоторое свойство объекта MyData.

Ну либо как-то по-другому подойти к вопросу выбора шаблонов. К примеру, сделать шаблоны элементами комбобокса и напрямую назначать в ContentPresenter без всяких промежуточных TemplateSelector-ов
20 фев 19, 13:20    [21815348]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Qwe.Qwe1
Member

Откуда:
Сообщений: 232
Вроде бы так и делаю, установил для комбобокса SelectedValue на свойство ViewModel'и SelectedDocTypeID, при выборе значения - я попадаю в сеттер, где делаю OnPropertyChanged("MyObject"); подразумевая, что ContentPresenter выберет другой шаблон, но ничего не происходит.
Хотя если указать SelectedDocTypeID в качестве контента ContentPresenter, то я захожу в связанный TemplateSelector. То есть реакция есть. При чем SelectedDocTypeID и MyObject - два свойства одной и той же VM. Почему так?
20 фев 19, 14:51    [21815475]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 19953
Еще предыдущее сообщение пришлось напрячься, чтобы понять. А теперь Шаляпин на пальцах.
Код давай, короче ))
20 фев 19, 15:04    [21815489]     Ответить | Цитировать Сообщить модератору
 Re: DataTemplate и ViewModel  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 19953
Qwe.Qwe1
- я попадаю в сеттер, где делаю OnPropertyChanged("MyObject"); подразумевая, что ContentPresenter выберет другой шаблон, но ничего не происходит.
возможно потому, что реально объект не заменился. Попробуй присвоить свойству null, а потом опять объект... хотя это костыль, конечно
20 фев 19, 15:18    [21815499]     Ответить | Цитировать Сообщить модератору
Все форумы / WPF, Silverlight Ответить