C#:
class ViewModelsWindow2 : INotifyPropertyChanged
{
private List<BookBase> _listBase;
private ObservableCollection<Tab> _tabs;
public List<BookBase> ListBase {...} // It's for work and serialization to file.
public ObservableCollection<Tab> Tabs {...} // It's for binding to WPF.
public ViewModelsWindow2()
{
ListBase = new List<BookBase>()
{
new BookBase() { Author="Mark Twain", Name="The Adventures of Tom Sawyer"},
new BookBase() { Author="Mark Twain", Name="Adventures of Huckleberry Finn"}
};
var newTab = new Tab()
{
Header = "New",
BookBase = ListBase,
AddBook = new Command(o => { MessageBox.Show("!"); ListBase.Add(new BookBase() { Author = "", Name = "" }); })
};
Tabs = new ObservableCollection<Tab>();
Tabs.Add(newTab);
}
}
Вспомогательные классы:
public class Tab
{
public string Header { get; set; }
public List<BookBase> BookBase { get; set; }
public Command AddBook { get; set; }
public Tab()
{
Header = null;
BookBase = new List<BookBase>();
AddBook = null;
}
}
public class BookBase
{
public string Author { get; set; }
public string Name { get; set; }
public BookBase()
{
Author = null;
Name = null;
}
}
XAML:
<Window
x:Class="WpfApp1.Views.Window2"
...
xmlns:local="clr-namespace:WpfApp1.Views"
...
xmlns:vm="clr-namespace:WpfApp1.ViewModels"
...
mc:Ignorable="d">
<Window.DataContext>
<vm:ViewModelsWindow2 />
</Window.DataContext>
<Grid>
<TabControl Grid.Column="0" ItemsSource="{Binding Tabs}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="25" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Button Command="{Binding AddBook}" Content="Continue" />
</Grid>
<DataGrid Grid.Row="1" AutoGenerateColumns="False" CanUserAddRows="False" ItemsSource="{Binding BookBase}">
<DataGrid.Columns>
<DataGridTextColumn MinWidth="70" Binding="Binding Author}" Header="Author" />
<DataGridTextColumn MinWidth="100" Binding="{Binding Name}" Header="Name" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>
Вообщем не получается заставить эту программу нормально работать, решил обратиться к сообществу.
Суть в том, что не работает кнопка добавления строк. Также не влияет добавляю я к ObservableCollection
или к List
. Кнопка не работает при обоих вариантах.
Восросы:
AddBook
находилась в основном класе, а не в классе Tab
, а вызывальсь из XAML с того же места, что и сейчас. Не вижу смысла плодить комманду, если она делает одну и ту же функцию. Скажу проще, возможно изменить XAML, что бы перенисти комману в основной класс?Также не влияет добавляю я к ObservableCollection или к List . Кнопка не работает при обоих вариантах.
Основная ваша проблема в том, что вы привязываетесь к List<T>
, которому зачем то пытаетесь реализовать не тот интерфейс. List<T>
- что это, коллекция или свойство? А INotifyPropertyChanged
- за что отвечает, за коллекцию или за свойство?
Вердикт таков: Хотите мучать мозг - реализовывайте INotifyCollectionChanged
, не хотите - переделывайте все на уже реализующие данный интерфейс коллекции (ObservableCollection
или BindingList
). В данном случае вы данные добавляете, но интерфейс не знает о том, что коллекция обновилась.
P.S. Проверил весь ваш код изменил все List<T>
на ObservableCollection<T>
и о чудо, данные появляются!.
Хорошо, давайте теперь поговорим о коде:
ICommand
интерфейса, а не класса, который ее реализует.INotifyPropertyChanged
лучше вынести в отдельный класс (дабы не дублировать его по сотни раз). P.S. Я его вынесу, но он вам не нужен, по этому закомментирую его наследование.Исходя из этого мы получаем такой код:
public class Tab
{
public string Header { get; set; }
public ObservableCollection<BookBase> BookBase { get; set; }
public ICommand AddBook { get; set; }
public Tab(string header, ObservableCollection<BookBase> bookBase, ICommand addBook = null)
{
Header = header;
BookBase = bookBase;
AddBook = addBook;
}
}
public class BookBase
{
public string Author { get; set; }
public string Name { get; set; }
public BookBase(string author, string name)
{
Author = author;
Name = name;
}
}
public class ViewBase : INotifyPropertyChanged
{
internal void RaisePropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class ViewModelsWindow2 /*: ViewBase*/
{
public ObservableCollection<BookBase> ListBase { get; set; }
public ObservableCollection<Tab> Tabs { get; set; }
private ICommand AddCommand { get; }
public ViewModelsWindow2()
{
AddCommand = new RelayCommand(Add);
ListBase = new ObservableCollection<BookBase>()
{
new BookBase("Mark Twain", "The Adventures of Tom Sawyer"),
new BookBase("Mark Twain", "Adventures of Huckleberry Finn")
};
Tabs = new ObservableCollection<Tab> { new Tab("New", ListBase, AddCommand) };
}
private void Add()
{
MessageBox.Show("!");
ListBase.Add(new BookBase("", ""));
}
}
Теперь что касается команды в главном классе:
В паттерне MVVM каждый VM должен отвечать за что то одно, у вас идет отдельно реализация VM вкладок и все, что касается именно вкладок должно быть внутри VM. Вы можете инициализировать команду в другом месте и передавать на нее ссылку в нужный VM (как собственно сделал я в коде выше), но не изменять пути путем XAML кода, это не правильно!
Виртуальный выделенный сервер (VDS) становится отличным выбором
Возникла задача отправлять email напрямую из программыПо мимо просто текста прикреплять файл лога к письму
Как выгрузить данные из databasebak в базу данных? Иcпользую ASP
Хотелось бы сократить время "вникания" в новый проект на 500+ классовЕсть ли какая то возможность в Unity, или просто в проекте Visual Studio, наглядно...