Передача команды / параметра в BackCode?

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

BackCode это вещь? - Знаю только о термине «код позади».

Rand Random 13.05.2018 16:03

Итак ... после прочтения текста вы фактически используете термин "код, стоящий за", по какой причине вы назвали его "BackCode" в заголовке вопроса? - И ... ваш текст содержит много шума (не относящийся к делу), что, прочитав его в первый раз, я подумал: «В чем проблема?» Возможно, перефразируйте текст без «истории» о том, как возникла проблема, и просто скажите, в чем заключается ваша проблема.

Rand Random 13.05.2018 16:08

Две кнопки пытаются добиться того же. Кнопка с «событием щелчка» работает с использованием кода позади, но кнопка, привязанная к ICommand в ViewModel (вызывая тот же метод в коде позади), не работает. Пользовательский интерфейс не обновляется при использовании ICommand / ViewModel. Поэтому я считаю, что либо неправильно вызываю метод из ViewModel, либо есть что-то особенное в обновлении пользовательского интерфейса, о котором я не знаю. (INotifyPropertyChanged не проблема)

mjordan 13.05.2018 16:38

@mjordan «не работает» бессмысленно. Вызывается ли метод execute? «INPC - не проблема» - не будьте так уверены в своих суждениях, когда вы находитесь на такой ранней стадии в изучении чего-либо. INPC, безусловно, является важной частью ответа.

15ee8f99-57ff-4f92-890c-b56153 13.05.2018 17:14

ОК. Из моего исходного вопроса: «Я могу подтвердить (с помощью messagebox.show), что мои данные передаются правильно». Я все время использую INPC и не верю, что это имеет значение, но скажите, пожалуйста, если я ошибаюсь. Правильное значение печатается в окне сообщения, поэтому я не верю, что проблема в INPC. Самоуверенность - не моя проблема, но я хотел бы получить ввод, который действительно отвечает на мой исходный вопрос («UI не обновляется с использованием новых данных, как это происходит, когда данные поступают из события Button_Click»). Благодарю.

mjordan 13.05.2018 20:33

Я нашел способ выполнить метод ViewModel из скрытого кода и получить статическое int, которое используется в скрытом коде. Code Behind обновляет интерфейс так, как я ожидал (в отличие от использования ICommand для выполнения метода в ViewModel). Я (добавил wpf x: name = "RefreshGantChart") И добавил в метод Button_Click следующее: Button RefreshGantChart = sender as Button; RefreshGantChart.Command.Execute (параметр RefreshGantChart.CommandPar‌); Продолжительность = ActiveJobViewModel.StaticDuration; Точно противоположно тому, что я пытался сделать, и ломает MVVM, но он работает

mjordan 13.05.2018 22:43

@mjordan Статическое целое число вместо возвращаемого значения. Думаю, теперь я понимаю основную проблему. Удачи.

15ee8f99-57ff-4f92-890c-b56153 13.05.2018 23:25
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
7
107
2

Ответы 2

Вы можете использовать платформу MVVMLight, это упростит вашу привязку. С помощью этого фреймворка вы можете просто создать свойство 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"/>

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

Вам необходимо устранить путаницу OP относительно того, как пользовательский элемент управления может отражать изменения в модели представления, вызванные командой. Я не думаю, что в его модели просмотра есть что-то, кроме команды, которая беспомощно манипулирует временным экземпляром пользовательского элемента управления.

15ee8f99-57ff-4f92-890c-b56153 13.05.2018 16:29

Я считаю, что ты прав, Эд. Я вижу, где я могу создать временный экземпляр, но я не знаю, как правильно манипулировать исходным пользовательским элементом управления.

mjordan 13.05.2018 16:47

Он может отразить изменение в своем пользовательском элементе управления с помощью mainViewModel. Ему просто нужно привязать свой пользовательский элемент управления к mainViewModel и использовать RaisePropertyChange? Или, может быть, я не очень хорошо понимаю вопрос

Belved 13.05.2018 17:07

@mjordan Это была бы хорошая возможность для вас изучить основы привязки данных с помощью INotifyPropertyChanged. Вам нужно всего лишь быть на шаг впереди ученика. Я многому научился, исследуя ответы здесь. (Упс, вы ОП, но действует тот же принцип).

15ee8f99-57ff-4f92-890c-b56153 13.05.2018 17:10

На ваш взгляд (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();
    }
}

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

Теперь, когда у вас есть виртуальная машина, нам нужно снова привязать ее к 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 раньше, тем лучше. Кодирование WPF в старом стиле Forms (много кода позади) не будет использовать величие WPF.

На первый взгляд это выглядит неплохо, но Windows.DataContext и value as int не компилируются.

15ee8f99-57ff-4f92-890c-b56153 14.05.2018 15:33

протестируйте свой код. Попробуй это. Пожалуйста. int - это тип значения. Double - это тип значения.

15ee8f99-57ff-4f92-890c-b56153 15.05.2018 03:10

Если OP не может заставить работать свой код, как именно вы ему помогаете, давая ему код, который даже не компилируется? Помимо этой проблемы, как targetType == typeof(SolidColorBrush)Когда-либо будет правдой?

15ee8f99-57ff-4f92-890c-b56153 15.05.2018 15:44

Спасибо за ваш ответ. Это вдохновило меня переосмыслить свой подход.

mjordan 15.05.2018 15:53

Другие вопросы по теме