2010-02-21 31 views
11

Tôi đang viết một ứng dụng WPF 4 (với VS2010 RC) sử dụng MVVM nhẹ V3 alpha 3 và đang chạy vào một số hành vi kỳ lạ ở đây ...CanExecute trên RelayCommand <T> không làm việc

Tôi có một lệnh mở ra một Window và Cửa sổ đó tạo ViewModel và v.v. - không có gì lạ ở đó.

Trong Window rằng tôi có một số RelayCommand s, ví dụ:

CategoryBeenSelected = new RelayCommand(() => OnCategoryUpdate = true); 

Không có gì lạ một lần nữa - nó hoạt động như tôi mong đợi.

Vấn đề là tôi không thể có biểu thức CanExecute method/lambda với một RelayCommand chung.

này hoạt động:

DeleteCategoryCommand = new RelayCommand<int>(DeleteCategory); 

Nhưng điều này không:

DeleteCategoryCommand = new RelayCommand<int>(DeleteCategory, CanDeleteCategory); 

Window không hiển thị. Ý tôi là, tôi nhấp vào nút mở cửa sổ và ứng dụng sẽ bị chặn và một vài giây sau, phương thức InitializeComponent của Window sẽ ném một số NullReferenceException (Tham chiếu đối tượng không được đặt thành thể hiện của đối tượng)

Tóm lại, Nếu Tôi đặt một phương thức CanExecute trên RelayCommand<T>, Window rằng sở hữu rằng ViewModel (với RelayCommand<T>) không thể được khởi tạo. Nếu tôi xóa CanExecute, thì Window sẽ hiển thị.

Sự cố ở đây ở đâu? Tôi bối rối.

Cảm ơn bạn.

EDIT: Theo yêu cầu, đây là stack trace:

 
A first chance exception of type 'System.NullReferenceException' occurred in PresentationFramework.dll 
    at GalaSoft.MvvmLight.Command.RelayCommand`1.CanExecute(Object parameter) 
    at System.Windows.Controls.Primitives.ButtonBase.UpdateCanExecute() 
    at System.Windows.Controls.Primitives.ButtonBase.OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e) 
    at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e) 
    at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args) 
    at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType) 
    at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal) 
    at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value) 
    at MS.Internal.Xaml.Runtime.ClrObjectRuntime.SetValue(Object inst, XamlMember property, Object value) 
    at MS.Internal.Xaml.Runtime.PartialTrustTolerantRuntime.SetValue(Object obj, XamlMember property, Object value) 
    at System.Xaml.XamlObjectWriter.Logic_ApplyPropertyValue(ObjectWriterContext ctx, XamlMember prop, Object value, Boolean onParent) 
    at System.Xaml.XamlObjectWriter.Logic_DoAssignmentToParentProperty(ObjectWriterContext ctx) 
    at System.Xaml.XamlObjectWriter.WriteEndObject() 
    at System.Windows.Markup.WpfXamlLoader.TransformNodes(XamlReader xamlReader, XamlObjectWriter xamlWriter, Boolean onlyLoadOneNode, Boolean skipJournaledProperties, Boolean shouldPassLineNumberInfo, IXamlLineInfo xamlLineInfo, IXamlLineInfoConsumer xamlLineInfoConsumer, XamlContextStack`1 stack, IStyleConnector styleConnector) 
    at System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, Boolean skipJournaledProperties, Object rootObject, XamlObjectWriterSettings settings, Uri baseUri) 
    at System.Windows.Markup.WpfXamlLoader.LoadBaml(XamlReader xamlReader, Boolean skipJournaledProperties, Object rootObject, XamlAccessLevel accessLevel, Uri baseUri) 
    at System.Windows.Markup.XamlReader.LoadBaml(Stream stream, ParserContext parserContext, Object parent, Boolean closeStream) 
    at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator) 
    at ApuntaNotas.Views.CategoryEditorView.InitializeComponent() in c:\Users\Jesus\Documents\Visual Studio 2010\Projects\ApuntaNotas\ApuntaNotas\Views\CategoryEditorView.xaml:line 1 
    at ApuntaNotas.Views.CategoryEditorView..ctor() in C:\Users\Jesus\Documents\Visual Studio 2010\Projects\ApuntaNotas\ApuntaNotas\Views\CategoryEditorView.xaml.cs:line 18 
A first chance exception of type 'System.NullReferenceException' occurred in PresentationFramework.dll 
+1

Có thể bạn có thể đính kèm theo dõi ngăn xếp? Nó có thể giúp hiểu những gì đã xảy ra. – Vlad

+0

Xin lỗi, tôi quên rằng, có nó là :) –

+0

Thật lạ: Reflector nói rằng hàm 'CanExecute' được định nghĩa theo cách như sau:' public bool CanExecute (tham số đối tượng) {return (this._canExecute == null) | | this._canExecute ((T) tham số)); } '. Không có gì để ném một ngoại lệ. – Vlad

Trả lời

7

Dường như RelayCommand sẽ bỏ giá trị tham số cho T. generic

Nhưng bạn không thể bỏ null để một cấu trúc, như một ngoại lệ cho bạn!

Nếu bạn khởi tạo RelayCommand với cấu trúc không có giá trị, nó sẽ hoạt động như mong đợi!

RelayCommand<int?> or RelayCommand<Nullable<int>> 

HTH

+0

Uhm, đó phải là lý do ... Nhưng đó là một chút lạ .. Tôi không thấy bất kỳ mã nào sử dụng nullable ... –

+0

Đúng vậy .. a 'double' hoặc' int' là các loại giá trị, và không thể rỗng. Nếu bạn làm cho chúng có các loại nullable, nó sẽ hoạt động. Việc đưa 'null' vào một cấu trúc sẽ tạo ra một ngoại lệ! Xem Vlads bình luận với phương pháp mà bạn có thể thấy các diễn viên để T! – Arcturus

+0

thử biên dịch kiểm tra kép = (đôi) null; .. trong thế giới chung bạn sẽ nhận được một ngoại lệ thời gian chạy! ;) – Arcturus

0

Có lẽ, vào thời điểm này, tham số là null?

2

Arcturus là đúng trong việc xác định những gì vấn đề là, tuy nhiên tôi không thích giải pháp của việc sử dụng nguyên thủy nullable. Cá nhân tôi không thích những nguyên thủy vô giá trị trừ khi tôi có một lý do rất tốt để sử dụng chúng.

Thay vào đó, tôi đã thay đổi việc thực hiện RelayCommand như sau:

bool ICommand.CanExecute(object parameter) 
    { 
     if (parameter == null && typeof(T).IsValueType) 
     { 
      return CanExecute(default(T)); 
     } 
     return CanExecute((T)parameter); 
    } 

tôi không thực hiện thay đổi này tương tự cho các phương pháp Execute generic (ít nhất là cho bây giờ) bởi vì tôi không nghĩ rằng đó là không hợp lý thất bại trong trường hợp đó nếu lệnh thực sự mong đợi một đối số.

Vấn đề với CanExecute là hệ thống WPF đôi khi sẽ gọi nó trước khi các ràng buộc nhất định có thể được đánh giá. Ví dụ:

 <Button Content="Fit To Width" Command="{Binding Path=FitToWidthCommand}" CommandParameter="{Binding ElementName=imageScrollViewer, Path=ActualWidth}" /> 
     <Button Content="Fit To Height" Command="{Binding Path=FitToHeightCommand}" CommandParameter="{Binding ElementName=imageScrollViewer, Path=ActualHeight}" /> 

Trong XAML ở trên, bạn nhận thấy tham số lệnh được gắn với chiều rộng thực tế của điều khiển. Tuy nhiên, WPF sẽ gọi CanExecute trên lệnh của nút trước khi điều khiển "imageScrollViewer" nhất thiết phải được đặt ra/trả về - vì vậy không có chiều rộng/chiều cao thực tế. Bởi thời gian người dùng nhấp vào nút và thực hiện được gọi, tất nhiên điều khiển được đặt ra để các giá trị được gửi đến lệnh. Nếu không - tôi nghĩ thất bại là những gì nên được mong đợi - nhưng chỉ khi người dùng thực sự nhấp vào nút.

Tất nhiên tôi không thích các hành vi khác nhau của CanExecute và Execute, nhưng bây giờ nó có vẻ phù hợp với các hạn chế được trình bày bởi khung công tác. Tôi có thể tìm thấy một kịch bản mà điều này làm tôi đau buồn, nhưng tôi đã thích sự thay đổi cho đến nay.

Các vấn đề liên quan