自学并挣扎于可能是一个基本概念。所以任何额外的解释都是受欢迎的......我有以下代码,它们完美地适用于Button_Click事件和Code Behind。我可以使用一个小代码,因为它基本上只是操纵视图。但是,我想从ViewModel获取数据/业务逻辑。我能够确认(使用messagebox.show)我的数据正确传递,但是UI不会像使用来自Button_Click事件的数据那样使用新数据进行更新。我已经有一段时间困难了,并继续寻找工作。据我所知,我的方法完全错了。我真的很想知道如何做到这一点。
WPF
<StackPanel Grid.Row="1" VerticalAlignment="Bottom">
<TextBox x:Name="NumberOfDays" HorizontalAlignment="Left" Background="GreenYellow" Foreground="Black">
</TextBox>
<StackPanel Orientation="Horizontal">
<Button Content="change hello row" Click="Button_Click" HorizontalAlignment="Left" Background="GreenYellow" Foreground="Black">
</Button>
<Button Content="TestCommand5" Margin="0 0 0 0" Padding="5" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" FontSize="16"
CommandParameter="{Binding objActiveJobClass.JobID}"
Command="{Binding TestCommand5}"
IsTabStop = "False" FocusVisualStyle="{DynamicResource MyFocusVisual}"
Style="{Binding Mode=OneWay, Source={StaticResource NavigationButtonStyle}}">
</Button>
</StackPanel>
<Label Content="Gant Chart Area" HorizontalAlignment="Left"/>
<ScrollViewer Width="1200" HorizontalAlignment="Left" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<Grid >
<Canvas x:Name="GantChartCanvas" HorizontalAlignment="Left" Background="Yellow" Height="405" />
</Grid>
</ScrollViewer>
</StackPanel>
BackCode:
public partial class GantChartUserControl : UserControl
{
public GantChartUserControl()
{
InitializeComponent();
}
public GantChartUserControl(int Duration)
{
InitializeComponent();
CreateTimeLine(Duration);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
int variable = 0;
if (NumberOfDays != null)
{
int.TryParse(NumberOfDays.Text, out variable);
if (variable > 0)
{
CreateTimeLine(variable);
}
else
{
MessageBox.Show($"\"{NumberOfDays.Text}\" is not an INTEGER greater than Zero.");
}
}
}
public void CreateTimeLine(int duration)
{
MessageBox.Show($"CreateTimeLine duration {duration}");
GantChartCanvas.Children.Clear();
double ControlWidth = 100d;
int Duration = duration;
for (int i = 0; i < Duration; i++)
{
Label u1 = new Label();
u1.HorizontalAlignment = HorizontalAlignment.Left;
u1.VerticalAlignment = VerticalAlignment.Bottom;
u1.HorizontalContentAlignment = HorizontalAlignment.Center;
u1.VerticalContentAlignment = VerticalAlignment.Center;
u1.Background = IsOdd(i) ? Brushes.Gray : Brushes.DarkGray;
u1.Height = 30;
u1.Width = ControlWidth;
u1.SetValue(Canvas.LeftProperty, (ControlWidth * i));
u1.SetValue(Canvas.BottomProperty, 0d);
u1.Content = string.Concat("LABEL ", i + 1);
GantChartCanvas.Width = Duration * ControlWidth;
GantChartCanvas.Children.Add(u1);
}
}
public static bool IsOdd(int value)
{
return value % 2 != 0;
}
}
视图模型
private ICommand _TestCommand5;
public ICommand TestCommand5
{
get
{
if (_TestCommand5 == null)
{
_TestCommand5 = new RelayCommand<object>(ExecuteTestCommand5, CanExecuteTestCommand5);
}
return _TestCommand5;
}
}
public bool CanExecuteTestCommand5(object parameter)
{
return true;
}
public void ExecuteTestCommand5(object parameter)
{
Debug.WriteLine($"\nDataBaseHelperClass: {System.Reflection.MethodBase.GetCurrentMethod()}");
int testint = 44;
GantChartUserControl objGantChartUserControl = new GantChartUserControl();
objGantChartUserControl.CreateTimeLine(testint);
}
答案 0 :(得分:0)
您可以使用MVVMLight框架,它可以简化您的绑定。
使用此框架,您只需创建属性public RelayCommand YourButton{ get; set; }
在ViewModel的构造函数中,您只需添加此行
YourButton = new RelayCommand(Method); // Method is the method to call when you click on the button
之后,您只需要绑定属性上的按钮。
一个小例子:
视图模型:
public RelayCommand MyButton { get; set; }
public MainViewModel()
{
MyButton = new RelayCommand(Action);
}
public void Action(){
Console.Writeline("Button Pressed");
}
WPF
<Button Command="{Binding MyButton}" Content="Button"/>
当你使用绑定时,它会更加美观,以最大限度地减少你的代码。
答案 1 :(得分:-1)
在您的视图(xaml)中,首先将VM设置为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>
...
View(UserControl)不应该打扰任何数据操作,而只是显示(绑定)ViewModel的属性,该属性是专门公开的。我真的建议把所有这些代码都写在ViewModel中。那么,你想要在Code Behind(xaml.cs)中拥有的是:
public partial class GantChartUserControl : UserControl
{
public GantChartUserControl()
{
InitializeComponent();
}
}
ViewModel:
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);
}
}
}
现在您已准备好VM,我们需要将其绑定回View。所以,回到你的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。使用旧Forms
样式(大量代码隐藏)编写WPF并不能充分利用WPF的优势。