WPF - วิธีบังคับให้คำสั่งประเมิน 'CanExecute' อีกครั้งผ่าน CommandBindings


130

ฉันมีMenuที่ซึ่งแต่ละMenuItemลำดับชั้นมีCommandคุณสมบัติที่กำหนดเป็นที่RoutedCommandฉันกำหนดไว้ ที่เกี่ยวข้องCommandBindingให้โทรกลับสำหรับการประเมินผลของซึ่งควบคุมการเปิดการใช้งานของแต่ละรัฐCanExecuteMenuItem

นี้เกือบจะทำงาน รายการเมนูเริ่มต้นด้วยสถานะที่เปิดใช้งานและปิดใช้งานที่ถูกต้อง อย่างไรก็ตามเมื่อข้อมูลที่การCanExecuteโทรกลับของฉันใช้เปลี่ยนแปลงฉันต้องการคำสั่งเพื่อขอผลลัพธ์จากการโทรกลับอีกครั้งเพื่อให้สถานะใหม่นี้แสดงใน UI

ดูเหมือนจะไม่มีวิธีการสาธารณะใด ๆ ในRoutedCommandหรือCommandBindingสำหรับสิ่งนี้

โปรดทราบว่าการโทรกลับจะถูกใช้อีกครั้งเมื่อฉันคลิกหรือพิมพ์ลงในตัวควบคุม (ฉันเดาว่ามันถูกทริกเกอร์จากอินพุตเนื่องจากการวางเมาส์เหนือไม่ทำให้เกิดการรีเฟรช)

คำตอบ:


172

ไม่ใช่สิ่งที่สวยที่สุดในหนังสือ แต่คุณสามารถใช้ CommandManager เพื่อยกเลิกการผูกคำสั่งทั้งหมด:

CommandManager.InvalidateRequerySuggested();

ดูข้อมูลเพิ่มเติมเกี่ยวกับMSDN


1
ขอบคุณสิ่งนี้ใช้ได้ดี มีความล่าช้าเล็กน้อยใน UI แต่ฉันไม่กังวลกับเรื่องนี้มากเกินไป นอกจากนี้ฉันโหวตคำตอบของคุณทันทีจากนั้นจึงกลับมาโหวตเพื่อดูว่าได้ผลหรือไม่ ตอนนี้ใช้งานได้แล้วฉันไม่สามารถสมัครโหวตซ้ำได้อีก ไม่แน่ใจว่าเหตุใด SO จึงมีกฎดังกล่าว
Drew Noakes

5
ฉันแก้ไขคำตอบของคุณเพื่อใช้การโหวตของฉันอีกครั้ง ฉันไม่ได้เปลี่ยนแปลงอะไรในการแก้ไข ขอบคุณอีกครั้ง.
Drew Noakes

ฉันมีปัญหาเดียวกันนี้เกิดขึ้นเมื่อฉันเปลี่ยนเนื้อหาของ Texbox จากโค้ดที่อยู่เบื้องหลัง หากคุณแก้ไขด้วยมือมันจะได้ผล ในแอพนี้พวกเขามี Texbox ที่ถูกแก้ไขโดยตัวควบคุมที่จะป๊อปอัปและเมื่อคุณบันทึกป๊อปอัปมันจะเปลี่ยนคุณสมบัติ Texbox.Text วิธีนี้ช่วยแก้ปัญหาได้! ขอบคุณ @Arcturus
Dzyann

10
เพียงสังเกตจากคำตอบอื่น ๆ ( stackoverflow.com/questions/783104/refresh-wpf-command ) "จะต้องถูกเรียกในเธรด UI"
Samvel Siradeghyan

84

เผื่อใครเจอทีหลัง; หากคุณใช้ MVVM และ Prism DelegateCommandการใช้งานของ Prism ICommandจะมี.RaiseCanExecuteChanged()วิธีการทำเช่นนี้


12
รูปแบบนี้พบได้ในไลบรารี MVVM อื่น ๆ เช่น MVVM Light
Peter Lillevold

2
ซึ่งแตกต่างจากปริซึมซอร์สโค้ด MVVM แสง v5 บ่งชี้ของมันเพียงแค่โทรRaiseCanExecuteChanged() CommandManager.InvalidateRequerySuggested()
Peter

4
หมายเหตุด้านข้างของ MVVM Light ใน WPF คุณต้องใช้เนมสเปซ GalaSoft.MvvmLight.CommandWpf ตั้งแต่ GalaSoft.MvvmLight.Command จะทำให้เกิดปัญหาmvvmlight.net/installing/changes#v5_0_2
fuchs777

((RelayCommand)MyCommand).RaiseCanExecuteChanged();ใช้งานได้สำหรับฉันโดยใช้ GalaSoft.MvvmLight.Command - แต่หลังจากเปลี่ยนเป็นCommandWPFมันใช้งานได้โดยไม่จำเป็นต้องโทรหาอะไรเลย ขอบคุณ @ fuchs777
Robin Bennett

1
จะเป็นอย่างไรถ้าคุณไม่ได้ใช้ไลบรารีของบุคคลที่สามล่ะ?
Vidar

28

ฉันไม่สามารถใช้งานได้CommandManager.InvalidateRequerySuggested();เนื่องจากฉันได้รับผลกระทบจากประสิทธิภาพ

ฉันใช้คำสั่ง Delegating ของMVVM Helperซึ่งมีลักษณะดังนี้ (ฉันได้ปรับแต่งเล็กน้อยสำหรับความต้องการของเรา) คุณต้องโทรcommand.RaiseCanExecuteChanged()จาก VM

public event EventHandler CanExecuteChanged
{
    add
    {
        _internalCanExecuteChanged += value;
        CommandManager.RequerySuggested += value;
    }
    remove
    {
        _internalCanExecuteChanged -= value;
        CommandManager.RequerySuggested -= value;
    }
}

/// <summary>
/// This method can be used to raise the CanExecuteChanged handler.
/// This will force WPF to re-query the status of this command directly.
/// </summary>
public void RaiseCanExecuteChanged()
{
    if (canExecute != null)
        OnCanExecuteChanged();
}

/// <summary>
/// This method is used to walk the delegate chain and well WPF that
/// our command execution status has changed.
/// </summary>
protected virtual void OnCanExecuteChanged()
{
    EventHandler eCanExecuteChanged = _internalCanExecuteChanged;
    if (eCanExecuteChanged != null)
        eCanExecuteChanged(this, EventArgs.Empty);
}

3
เพียงแค่ FYI ฉันแสดงความคิดเห็น CommandManager.RequerySuggested + = value; ฉันได้รับการประเมินค่า CanExecute ใกล้เคียง / วนซ้ำของโค้ดด้วยเหตุผลบางประการ มิฉะนั้นการแก้ปัญหาจะทำงานตามที่คาดไว้ ขอบคุณ!
robaudas

16

หากคุณได้เปิดตัวคลาสของคุณเองที่ใช้ICommandคุณอาจสูญเสียการอัปเดตสถานะอัตโนมัติจำนวนมากซึ่งบังคับให้คุณต้องพึ่งพาการรีเฟรชด้วยตนเองมากกว่าที่ควรจะเป็น InvalidateRequerySuggested()นอกจากนี้ยังสามารถทำลาย ปัญหาคือการICommandใช้งานอย่างง่ายไม่สามารถเชื่อมโยงคำสั่งใหม่กับไฟล์CommandManager.

วิธีแก้ปัญหาคือใช้สิ่งต่อไปนี้:

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void RaiseCanExecuteChanged()
    {
        CommandManager.InvalidateRequerySuggested();
    }

วิธีนี้ทำให้สมาชิกแนบCommandManagerมากกว่าชั้นเรียนของคุณและสามารถมีส่วนร่วมในการเปลี่ยนแปลงสถานะคำสั่งได้อย่างเหมาะสม


2
ตรงไปตรงมาตรงประเด็นและช่วยให้ผู้คนสามารถควบคุมการใช้งาน ICommand ได้
Akoi Meexx

2

ฉันได้ใช้วิธีแก้ปัญหาเพื่อจัดการการพึ่งพาคุณสมบัติของคำสั่งที่นี่ลิงค์https://stackoverflow.com/a/30394333/1716620

ต้องขอบคุณที่คุณจะมีคำสั่งเช่นนี้:

this.SaveCommand = new MyDelegateCommand<MyViewModel>(this,
    //execute
    () => {
      Console.Write("EXECUTED");
    },
    //can execute
    () => {
      Console.Write("Checking Validity");
       return PropertyX!=null && PropertyY!=null && PropertyY.Length < 5;
    },
    //properties to watch
    (p) => new { p.PropertyX, p.PropertyY }
 );

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.