У меня есть xml файл
<ballast>
<tank number="32" ID="FPTW" Name="Forepeak" Weight="0,00" SG="1,025" Volume="0,00">
<volume level="0.00" x="52.00" y="-0.90" z="0.00">0.00</volume>
<volume level="0.10" x="53.48" y="0.00" z="0.05">0.77</volume>
<volume level="0,20" x="53,70" y="0,00" z="0,11">1,99</volume>
<volume level="0,30" x="53,88" y="0,00" z="0,18">3,61</volume>
<volume level="0,40" x="54,02" y="0,00" z="0,24">5,54</volume>
<volume level="0,50" x="54,16" y="0,00" z="0,30">7,80</volume>
<volume level="0,60" x="54,29" y="0,00" z="0,36">10,36</volume>
<volume level="0,70" x="54,39" y="0,00" z="0,42">13,12</volume>
</tank>
</ballast>
Есть модель
public class Model
{
public static string Path = "XMLFile1.xml";
}
[Serializable]
public class ModelBallast
{
public int Number { get; set; }
public string Id { get; set; }
public string Name { get; set; }
public double Weight { get; set; }
public double Sg { get; set; }
public double Volume { get; set; }
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
}
Есть vm но в ней только INotifyPropertyChanged
class BallastVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Есть view
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TabControl>
<TabItem Header="Ballast" Height="40" Width="100">
<DataGrid x:Name="ballastdg" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Width="3*"/>
<DataGridTextColumn Header="Weight" Width="*"/>
<DataGridTextColumn Header="SG" Width="*"/>
<DataGridTextColumn Header="Volume" Width="*"/>
</DataGrid.Columns>
</DataGrid>
</TabItem>
</TabControl>
<DataGrid Grid.Row="1" x:Name="dcdg" >
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Width="3*" />
<DataGridTextColumn Header="Volume" Width="*"/>
<DataGridTextColumn Header="X" Width="*"/>
<DataGridTextColumn Header="Y" Width="*"/>
<DataGridTextColumn Header="Z" Width="*"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
И мне надо
Для начала подготовим проект:
Models
и ViewModels
. ViewModels
создадим новый класс, который будет реализовывать INotifyPropertyChanged
, назовем его к примеру BaseVM
и перенесем туда весь ваш код.ViewModels
создадим еще класс, который укажем как DataContext
, у меня будет это MainViewModel
.MainWindow.xaml.cs
зададим DataContext
. После InitializeComponent();
пишем DataContext = new MainViewModel();
.Все, базовую подготовку мы сделали, теперь по порядку:
Binding xml на ballastdg
Xml по сути можно считать моделью, которая будет иметь методы для загрузки данных и их отдачи в VМ.
Models
класс DataModel
(имена указывайте реальные, у меня это чисто для примера), а также класс ItemModel
.В классе ItemModel
я пропишу все необходимые для примера свойства:
class ItemModel
{
public int Id { get; set; }
public string Name { get; set; }
public int Value { get; set; }
}
В DataModel
сделаем имитацию получения данных, пусть отдает нам некоторые тестовые данные:
class DataModel
{
public List<ItemModel> GetItems() =>
new List<ItemModel>
{
new ItemModel(1, "Item 1", 33),
new ItemModel(2, "Item 2", 14),
new ItemModel(2, "Item 3", 0),
};
}
Теперь мы имеем модель, которая готова отдавать нам данные, поработаем с ViewModel. Создадим в папке ViewModels
класс с названием ItemViewModel
и реализуем в нем все свойства для привязок и необходимую логику (если есть):
class ItemViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public int Value { get; set; }
}
В MainViewModel
подключаем модель, создаем публичное свойство нашей коллекции для привязки и сделаем метод для заполнения ее:
class MainViewModel
{
private DataModel Data;
public ObservableCollection<ItemViewModel> Items { get; set; }
public MainViewModel()
{
Data = new DataModel();
Items = new ObservableCollection<ItemViewModel>();
Load();
}
public void Load()
{
foreach (var item in Data.GetItems())
Items.Add(new ItemViewModel(item.Id, item.Name, item.Value));
}
}
Теперь View. Для удобства в Xaml укажем тип наших данных для дизайнера:
d:DataContext="{d:DesignInstance {x:Type vm:MainViewModel}}"
Напишем теперь простенькую разметку:
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Width="*" Binding="{Binding Id}"/>
<DataGridTextColumn Header="Название" Width="3*" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Значение" Width="*" Binding="{Binding Value}"/>
</DataGrid.Columns>
</DataGrid>
Все, ваши данные будут успешно отображены в приложении:
Вам тут останется реализовать сохранение значений при изменении и прочие мелочи.
Разрешить юзеру редактировать всё кроме имени с влиянием на другие элементы (weight = sg * volume)
Для запрета изменений сделайте свойство без Set
, допустим в моем примере убираем возможность изменить Id
:
ItemViewModel
переделываем Id
на public int Id { get; }
{Binding Id, Mode=OneWay}
.Для изменения других значений объекта в Set
нужного свойства добавьте логику, но не стоит забывать, что для оповещения интерфейса об изменении свойства, нужен INPC.
Давайте для примера я создам еще одно свойство, которое будет отображать значение умноженное на 2:
ItemViewModel
от ранее созданного BaseVM
- class ItemViewModel : BaseVM
Реализуем свойство с оповещением об изменении:
private int _customValue;
public int CustomValue
{
get => _customValue;
set
{
_customValue = value;
RaisePropertyChanged();
}
}
Изменяем старое свойство, добавляя ему нужную логику:
private int _value;
public int Value
{
get => _value;
set
{
_value = value;
CustomValue = value * 2;
}
}
Привязываем: <DataGridTextColumn Header="Значение x2" Width="*" Binding="{Binding CustomValue}"/>
Смотрим результат:
При volume !=0 показать строку в dcdg с тем показателем volume и соответствующим ему координатам
Для этих целей да, лучше использовать фильтрацию. Я покажу на примере ListCollectionView
.
В MainViewModel
создаем новое свойство, к которому будет привязан другой Control и в нем мы будем отображать только отфильтрованные значения:
public ListCollectionView FilteredItems { get; set; }
Далее в конструкторе MainViewModel
инициализируем это свойство, задав ему все что нам необходимо:
FilteredItems = new ListCollectionView(Items)
{
Filter = item => ((ItemViewModel)item).Value != 0,
IsLiveFiltering = true,
LiveFilteringProperties =
{
nameof (ItemViewModel.Value)
}
};
По аналогии с вашей View я сделаю снизу отфильтрованные данные:
<DataGrid Grid.Row="1" ItemsSource="{Binding FilteredItems}" AutoGenerateColumns="False" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Название" Width="3*" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Значение" Width="*" Binding="{Binding Value}"/>
</DataGrid.Columns>
</DataGrid>
Запускаем и смотрим результат:
Фуф, ну вроде все.
На будущее: пожалуйста, не задавайте несколько вопросов в одном!
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Нужна база с такими двумерными списками, как ее правильно записать? Вряд ли получится сделать public DbSet<List<List<string>>> lst { get; set; } (я не пробовал)
Подскажите пожалуйста, разрабатываю приложение WPF под FrameWork 40, т
Необходимо сделать фабрику (проще лямбду конечно), которая бы выдавала свободный dbcontext из пула