Windows Phone - Обвързване на TextBox или друга контрола към CommandParameter на Button

Правя първите си стъпки в използването на команди (чрез внедряване на интерфейса ICommand) в моите Windows Phone приложения. Сега се натъкнах на проблем, който изглежда не мога да разбера. Свързвам контрола, в този случай това е текстово поле, към свойството CommandParameter на Button:

<Button x:Name="BTN_Search"
        Style="{StaticResource ButtonNoPressedStyle}"
        BorderThickness="0"
        ccontrols:TiltEffect.IsTiltEnabled="True"
        Grid.Column="1"
        Height="85"
        Margin="0,0,0,-2"
        CommandParameter="{Binding ElementName=TB_Search}"
        Command="{Binding SearchTermCommand}">
        <Button.Background>
            <ImageBrush ImageSource="/Assets/Images/searchbtn.png" />
        </Button.Background>
</Button>

Когато приложението стартира и моделът на изглед се инстанцира, методът CanExecute се задейства два пъти подред.

public override bool CanExecute(object parameter)
    {
        if (parameter != null)
        {
            var textbox = parameter as TextBox;
            if ((textbox.DataContext as MainPageViewmodel).SearchTerm == null)
            {
                (textbox.DataContext as MainPageViewmodel).SearchTerm = "";
                return true;
            }
            else if (String.IsNullOrWhiteSpace(textbox.Text)) return false;
            else if (textbox.Text.Any(Char.IsDigit)) return false;
            else if (textbox.Text.Length < 4) return false;
            else if (_commandExecuting) return false;
            else
            {
                var bindingExpression = textbox.GetBindingExpression(TextBox.TextProperty);
                bindingExpression.UpdateSource();
                return true;
            }
        }
        return true;
    }

Първият път параметърът е null, а вторият път съдържа текстовото поле. Поради това поведение трябва да направя така, че тези първи два пъти методът CanExecute да връща true, в противен случай бутонът ще бъде деактивиран.

Четох някои други теми, че може да има връзка с RaiseCanExecuteChanged(), но и в това не съм толкова сигурен. Този въпрос има някои отговори относно този проблем, но отговорите не отговарят на нуждите ми, тъй като повечето решения са за WPF (с помощта на CommandManager или IMultiValueConverter), а други изглежда не работят.

Има ли някакво решение да се уверите, че CanExecute се задейства само веднъж или какво е обяснението за това поведение?


person Kaj    schedule 16.05.2014    source източник
comment
Изглежда като злоупотреба с шаблона ICommand за актуализиране на обвързващ израз в теста за CanExecute. Защо трябва да правите това?   -  person McGarnagle    schedule 17.05.2014
comment
Тъй като имах тази идея да актуализирам само когато е необходимо (когато всички изисквания са изпълнени). След като разгледах това, реших, че мога просто да добавя INotifyPropertyChanged към въпросното свойство. Благодаря ви, че ми обърнахте внимание. Но все пак бих искал да знам защо се случва това.   -  person Kaj    schedule 17.05.2014
comment
Не знам защо точно, но това предположение има смисъл за мен: първата проверка е, когато командният обект е конструиран за първи път; и втората проверка е, когато CommandParameter е обвързан (т.е. елементът TB_Search е зареден).   -  person McGarnagle    schedule 17.05.2014
comment
Това означава, че всяка контрола или свойство, свързано с командата, задейства RaiseCanExecuteChanged(), когато е заредено или актуализирано?   -  person Kaj    schedule 17.05.2014
comment
Добри въпроси, наистина не знам ... От опит знам, че промените на свойства чрез NotifyPropertyChanged не го задействат. Промени в свойствата на зависимостта? Промени във визуалното дърво? Никаква идея ...   -  person McGarnagle    schedule 17.05.2014
comment
Хммм, когато обвържа свойството SearchTerm (низ) директно към CommandParameter и задам това свойство в конструктора на моя модел на изглед, CanExecute също се задейства два пъти. Първо null и след това със стойността. Няма значение дали внедрявам INotifyPropertyChanged обаче... пропускам ли нещо тук?   -  person Kaj    schedule 17.05.2014


Отговори (2)


Мисля, че вашето текстово поле не се инициализира, когато командният параметър е ограничен за първи път. Опитайте да обвържете текстово поле с параметър viewmodel (параметърът трябва да е DependencyProperty или вашият изгледмодел трябва да имплементира INotifyPropertyChanged интерфейс) с двупосочен режим, след това предайте този параметър към командния параметър.

person Mike    schedule 16.05.2014

Този отговор не дава никакви категорични заключения, но след като тествах с различни свързвания на CommandParameter и разгледах информация, предоставена от Microsoft за интерфейса ICommand и свързаното CanExecuteChanged събитие, трябва да заключа, че това е свързано или с едно от следните, което Макгарнагъл вече предложи:

  • Промени във визуалното дърво.
  • Промени в свойство/обект на зависимост.

Това е правдоподобно, защото в документите се казва следното: „Случва се, когато възникнат промени, които влияят върху това дали командата трябва да се изпълни или не.“

Какво забелязах:

  • При обвързване на което и да е свойство на Viewmodel, CanExecute се задейства само веднъж при стартиране. Параметърът е null.
  • При обвързване на който и да е друг елемент или свойство на този елемент, CanExecute се задейства два пъти при стартиране. Параметърът е null първия път, а вторият път съдържа или елемента, или свойството на посочения елемент.
  • При обвързване на самия бутон {Binding RelativeSource={RelativeSource Mode=Self}}, CanExecute се задейства веднъж при стартиране и параметърът съдържа елемента Button.

Както казах преди, това не отговаря на нищо, но реших, че предоставянето на допълнителна информация по този въпрос няма да навреди. Ако някой намери окончателен отговор, предоставящ защо на това поведение, със сигурност ще го оценя.

Както и да е, използвах първия сценарий в собственото си приложение и преструктурирах логиката, така че сега работи, както първоначално възнамерявах.

person Kaj    schedule 21.05.2014