Передача команды / параметра в BackCode? - PullRequest
0 голосов
/ 13 мая 2018

Обучение самому себе и борьба с тем, что, вероятно, является основной концепцией.Так что любое дополнительное объяснение приветствуется ... У меня есть следующий код, который отлично работает от события Button_Click и кода позади.Я в порядке с использованием небольшого кода позади, потому что это в основном просто манипулирование видом.Тем не менее, я хочу получить данные / бизнес-логику из ViewModel.Я могу подтвердить (с messagebox.show), что мои данные передаются правильно, однако пользовательский интерфейс не обновляется с использованием новых данных, как это происходит, когда данные поступают из события 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;
    }       
}

ViewModel

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);
    }

Ответы [ 2 ]

0 голосов
/ 14 мая 2018

По вашему мнению (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.

0 голосов
/ 13 мая 2018

Вы можете использовать MVVMLight Framework, это упростит вашу привязку. С помощью этого фреймворка вы можете просто создать свойство public RelayCommand YourButton{ get; set; }

И в конструкторе вашей ViewModel вам просто нужно добавить эту строку YourButton = new RelayCommand(Method); // Method is the method to call when you click on the button

После этого вам просто нужно привязать свою кнопку к свойству.

Небольшой пример:

ViewModel:

    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"/>

Когда вы используете связывание, гораздо приятнее минимизировать ваш код.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...