2

Добрый вечер. Каким образом можно динамически добавлять в UcerCntrol элементы управления (TextBox'ы)? К примеру есть два объекта Line Circle

public class Line:IObjects
{
 private string _nameObject;
 public Point StartPoint(){get;set;}
 public Point EndPoint(){get;set;}

 public string NameObject{{return "Line";}{_nameObject = value;}}
}

public class Circle:IObjects
{
 private string _nameObject;
 public Point CenterPoint(){get;set;}
 public string NameObject{{return "Line";}{_nameObject = value;}}
}

interface IObject
{
string NameObject{get;set;}
}

Когда я получаю объекты интерфейса во View, необходимо отобразить параметры координат, т.к. в Line используется два параметра, а в Circle один то определяем сколько TextBox'ов необходимо отобразить для изменения параметров каждого объекта. Создать предварительно TextBox'ы в UC не логично, потому что при необходимости мы должны уметь создавать необходимое количество элементов в зависимости от редактируемого объекта.

KJfe
  • 123
  • А, причём здесь MVVM? – Streletz Nov 29 '17 at 16:06
  • @Streletz планируется использовать динамически добавленные элементы с данным патерном, т.е. из ViewModel передаем данные во View, но их необходимо правильно отразить, то есть есть несколько вариантов (приходят координаты, к примеру координаты линии (координаты начала и окончания линии) и круга (координаты центра)) но не красиво делать два TextBox для круга, вариант изменять видимость то же не подходит не продуктивно вдруг надо будет добавить многогранник n-граний. – KJfe Nov 29 '17 at 16:21
  • По вашему описанию абсолютно не ясно, что вы хотите. переформулируйте вопрос – tym32167 Nov 29 '17 at 17:31
  • просто задавайте точки в массиве, а не по одному свойству –  Nov 29 '17 at 18:54
  • @FoggyFinder сам факт хранения данных в двумерном массиве не решит проблему с изменением их из View пользователем, хотелось бы реализовать динамически создание элементов (к примеру TextBox) – KJfe Nov 29 '17 at 19:00
  • @KJfe так ведь и будет динамическое количество, в зависимости от элементов в массиве. Хотя не уверен, что понял почему в двумерном –  Nov 29 '17 at 19:58
  • @FoggyFinder у меня получается хранятся координаты по (x,y) начальной точки и конечной для линии, так же есть круг с координатами центра (x,y). Так же надо предполагается что в дальнейшем добавятся другие фигуры, для этого просто добавится новый класс с наследуемым интерфейсом. Вопрос в том как добавить элементы TextBox'a в WPF не в ручную прописывая в XAML и делая привязку (Binding) а динамически от определенных критерев. – KJfe Nov 29 '17 at 20:09
  • @FoggyFinder если использовать массив то его в принципе можно запихать в DataGrid и он отобразит все точки, но интересно можно ли как то для каждой координаты добавить TextBox – KJfe Nov 29 '17 at 20:10
  • 1
    Вам бы для начала изучить следующие понятия и для чего они используются: ObservableCollection, INotifyPropertyChanged, ItemsControl c ItemsControl.ItemTemplate. Первой пары ссылок в гугле хватит. По сути: 1) связать оба ваших класса в новом классе LineAndCircle. 2) Создать ObservableCollection<LineAndCircle>. 3) В интерфейсе создать ItemControl с нужным вам ItemTemplate. 4) Привязать ItemSource вашего ItemControl к ObservableCollection<LineAndCircle> – Иван Nov 29 '17 at 20:21
  • 2
    @John зачем мне связывать эти два класса, если я добавлю туда тысячу элементов фигур (многоугольник с 6 сторонами, треугольники, и всякая лажа, а вдруг еще добавятся и трехмерные фигуры, ломаные), я что их все буду связывать, бред, для этого у меня существует интерфейс, который я добавляю в коллекцию, затем получаю эти параметры из нее. – KJfe Nov 29 '17 at 20:24
  • @John А на счет ItemControl сейчас почитаю вроде может и помочь. – KJfe Nov 29 '17 at 20:32
  • @KJfe: А можно скетч того, как это должно выглядеть? – VladD Nov 29 '17 at 21:09

1 Answers1

1

Смотрите, если вы хотите показывать список объектов, для этого нужно и правда использовать ItemsControl и привязывать список к ItemsSource.

Обычно все объекты одинаковые, и тогда для отображения объекта подходит ItemTemplate. (Тут пример.) Но если ваш список разнородный, такой подход не пройдёт. Есть несколько разных путей решения. Можно использовать ItemTemplateSelector. Но если у объектов разные типы, то подойдёт такое простое решение:

<ItemsControl ItemsSource="{Binding тут-имя-вашей-коллекции}">
    <ItemsControl.Resources>
        <DataTemplate DataType="{x:Type vm:Line}">
            <!-- тут шаблон для отображения линии -->
        </DataTemplate>
        <DataTemplate DataType="{x:Type vm:Circle}">
            <!-- тут шаблон для отображения окружности -->
        </DataTemplate>
    </ItemsControl.Resources>
</ItemsControl>

Вот пример для DataContext = new object[] { 1, 2, "три", 4, "пять" }:

<ItemsControl ItemsSource="{Binding}">
    <ItemsControl.Resources>
        <DataTemplate DataType="{x:Type sys:String}">
            <TextBlock Text="{Binding StringFormat='String {0}'}"/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type sys:Int32}">
            <TextBlock Text="{Binding StringFormat='Int {0}'}"/>
        </DataTemplate>
    </ItemsControl.Resources>
</ItemsControl>

Результат:

вышел кролик погулять


Если вам нужно обработать объекты разного типа в «редакторе», можно использовать ту же технику, но с ContentPresenter'ом:

<ContentPresenter Content="{Binding CurrentObject}">
    <ContentPresenter.Resources>
        <DataTemplate DataType="{x:Type vm:Line}">
            <!-- тут шаблон для редактирования линии -->
        </DataTemplate>
        <DataTemplate DataType="{x:Type vm:Circle}">
            <!-- тут шаблон для редактирования окружности -->
        </DataTemplate>
    </ItemsControl.Resources>
</ItemsControl>
VladD
  • 206,799
  • @VledD Это конечно хорошо, но меня интересовал вопрос как организовать изменение данных для различных объектов, то есть при выборе объекта, открывалось бы поле где оно динамически заполнялось в зависимости от типа объекта и возможно было бы изменить данные. – KJfe Nov 30 '17 at 04:50
  • @KJfe: Дописал. – VladD Nov 30 '17 at 09:37
  • Вопрос а каким образом можно добавить переменную (vm) для моих объектов, или как то в ресуры надо запихать? – KJfe Nov 30 '17 at 10:58
  • @KJfe: Эээ, в какие-такие ресурсы? View не имеет права задавать себе VM. https://ru.stackoverflow.com/a/562586/10105 – VladD Nov 30 '17 at 11:45
  • На счет этого я знаю, я просто не могу понять vm это ссылка на ViewModel? – KJfe Nov 30 '17 at 11:51
  • @KJfe: Ну да. Но меня смущает слово «ссылка» — вы ведь не говорите про строковую переменную «ссылка на строку»? – VladD Nov 30 '17 at 11:56
  • Не я имел ввиду что View использует DataContext=экземпляр класса ViewModel, как то так – KJfe Nov 30 '17 at 12:22
  • @KJfe: Что означает «использует»? Вы хотите сказать, что View устанавливает себе DataContext? Нет, это плохой подход. – VladD Nov 30 '17 at 13:00
  • Нет, у меня есть ViewModelMain со свойством FirstVM{get;set;} для FirstUC. А в MainView у меня <ucerControl:FirstUC DataContext="{ Binding FirstVm}"/> как то так – KJfe Nov 30 '17 at 13:03
  • @KJfe: А, вот это правильный подход. Тогда непонятно, в чём ваш вопрос. – VladD Nov 30 '17 at 14:12
  • @KJfe: А, кажется понял. Вы про vm:? Нет, это не переменная, это namespace. Вы ж должны как-то сослаться на namespace, в котором определены ваши классы Circle и Line? – VladD Nov 30 '17 at 14:14
  • Вот я как раз про это, тоесть я должен записать namespace Model'и так как у меня классы там определены и наследуют интерфейс. – KJfe Nov 30 '17 at 14:18
  • @KJfe: Ну есть же стандартный метод подключать неймспейсы: xmlns:vm="clr-namespace:Program.Namespace1.Namespace2". Вот тут пример: https://ru.stackoverflow.com/q/576225/10105 – VladD Nov 30 '17 at 14:48