По вашему мнению (xaml), сначала установите виртуальную машину как ваш DataContext. Есть способы сделать это, но я собираюсь показать вам только один:
<UserControl x:Class="MyApp.GantChartUserControl"
...
xmlns:local="clr-namespace:MyApp"> <!--adjust the location(namespace.folder) of your VM as you like-->
<UserControl.DataContext>
<local:GantChartUserControlViewModel/> <!--this sets the GantChartUserControlVM as DataContext-->
</UserControl.DataContext>
...
Представление (UserControl) не должно беспокоить какие-либо манипуляции с данными, вместо этого оно просто показывает (привязывает) к свойству ViewModel, которое должно быть открыто. Я действительно предлагаю поместить весь этот код позади написанного вами в ViewModel. Итак, что вы хотите иметь в коде позади (xaml.cs):
public partial class GantChartUserControl : UserControl
{
public GantChartUserControl()
{
InitializeComponent();
}
}
Модель представления:
public class GantChartUserControlViewModel : INotifyPropertyChanged
{
// boiler-plate
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
// Property you want to expose:
public ObservableCollection<int> MyIntegers
{
get {return _myIntegers;}
set { _myIntegers = value; OnPropertyChanged("MyIntegers"); }
}
private ObservableCollection<int> _myIntegers;
// ICommand
public ICommand TestCommand5 { get; private set;}
// constructor
public GantChartUserControlViewModel()
{
MyInteger = new ObservableCollection<int>();
new RelayCommand<object>(ExecuteTestCommand5, CanExecuteTestCommand5);
}
public bool CanExecuteTestCommand5(object parameter)
{
return true;
}
public void ExecuteTestCommand5(object parameter)
{
Debug.WriteLine($"\nDataBaseHelperClass: {System.Reflection.MethodBase.GetCurrentMethod()}");
int testint = (int)parameter;
CreateTimeLine(testint);
}
private void CreateTimeLine(int duration)
{
MyIntegers.Clear();
for(int i=0;i<duration;i++)
{
MyIntegers.Add(i);
}
}
}
Теперь, когда у вас есть готовая виртуальная машина, нам нужно привязать ее к представлению. Итак, вернемся к вашему xaml, вместо использования Canvas, я рекомендую использовать ItemsControl (или ListView)
<ScrollViewer>
<ItemsControl ItemsSource={Binding MyIntegers, Mode=OneWay}>
<!--we have the integer collection, but View has the responsibility to set the layout, etc. which I usually call it the datatemplate-->
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label HorizontalAlignment="Left"
VerticalAlignment="Bottom"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Height="30"
Width="100">
<Label.Background>
<Binding>
<!--Why empty binding? it just means we are binding to the single int from the ItemsSource-->
<Binding.Converter>
<!--What is this converter? We'll get there later-->
<converter: IsOddToColorConverter/>
</Binding.Converter>
</Binding>
</Label.Background>
</Label>
</DataTemplate>
</ItemsControl.ItemTemplate>
<!-- I see that you wanted to have the labels put in horizontal, so we need to change the itemspanel from the default (vertical)-->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
<!-- you could use WrapPanel, in case if you want it to wrap, if it does not fit horizontally-->
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
Итак, приведенный выше конвертер - это инструмент для преобразования целого числа в связанном виде в цвет фона. Вам нужно будет создать конвертер другого класса:
/// <summary>This convert number to brush</summary>
public class IsOddToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo language)
{
// value is the integer input, we don't care about parameter or language
// output should be type of Brush
if (value is int && targetType == typeof(Brush))
{
bool isOdd = ((int)value) % 2 != 0;
return isOdd ? Brushes.Gray : Brushes.DarkGray;
}
// if input (and output) is unknown
return Brushes.Transparent;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo language)
{
// We don't care about this
throw new NotSupportedException();
}
}
Итак, как мы используем этот конвертер? Вернуться к вашему виду (xaml) снова. На уровне UserControl, пожалуйста, укажите путь
xmlns:converter="clr-namespace:MyApp.Converters" <!--if your converter you defined earlier is inside Converters folder-->
<!--since calling it as converter: IsoddToColorConverter will create a new instance. It might be better to declare this as a static resource, so memory usage won't grow, even if you are using it multiple times. For how to do it, please look in the internet-->
Тогда, вуаля, это должно соответствовать шаблону MVVM, и ваш код будет намного чище и проще в обслуживании.
Обратите внимание, что это самая основная идея того, что вам нужно сделать. В конце концов вы найдете несколько фреймворков (Prism
, MVVMLight
и т. Д.), Которые помогут вам сократить количество кодов, которые вам нужно написать. И я действительно рекомендую вам начать изучать MVVM раньше, тем лучше. Кодирование WPF со старым стилем Forms
(много кода) не будет использовать величие WPF.