0

私はWPFログインフォームを開発しています.2つのタブを持つタブコントロールがあります:

tab1) ログイン用の入力が含まれます (ユーザー名とパスワードのテキスト ボックス/ラベル)

tab2) 進行状況バーとして使用されるカスタム アニメーションが含まれています

ユーザーがすべての情報を取得し、[ログイン] ボタンのクリック イベントで [ログイン] をクリックすると、アクティブなタブが tab2 に設定され、進行状況バーがユーザーに表示されます。このステップでエラーが発生した場合、ユーザーを tab1 に戻したいのですが、ここで次のエラーが発生します。

無効な操作の例外(別のスレッドがこのオブジェクトを所有しているため、呼び出し元のスレッドはこのオブジェクトにアクセスできません。)

問題を解決するために、スレッドを強制終了する方法やその他の回避策をアドバイスしてください

私のコード:

public partial class LogonVM : ILogonVM
{
    private IWebService _webService;
    private static TabControl loaderTabs;

    private string userName = String.Empty;
    public string UserName
    {
        get { return userName; }
        set
        {
            userName = value;
            OnPropertyChanged("UserName", true);
        }
    }

    private SecureString password = new SecureString();
    public SecureString Password
    {
        get { return password; }
        set
        {
            password = value;
            OnPropertyChanged("Password", true);
        }
    }

    public MinimalLogonViewModel(MinimalLogonView view,IWebService webService)
    {
            _webService = webService;

            View = view;
            view.DataContext = this;

            loaderTabs = (TabControl)this.View.FindName("loaderTabs");
        }

        catch (Exception eX)
        {
            MessageBox.Show(eX.Message);
        }
    }

    protected virtual void OnPropertyChanged(string propertyName, bool raiseCanExecute)
    {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

            if (raiseCanExecute)
                LogonCommand.RaiseCanExecuteChanged();
    }

    private void Logon(object parameter)
    {
        SetActiveTab(TabType.Loader);

        _messageBroker.onAuthenticated += new EventHandler(_MessageBroker_onAuthenticated);
        Task.Execute((DispatcherWrapper)View.Dispatcher,
                     () => _webService.Authenticate(userName, password.ConvertToUnsecureString()),
                     (ex) =>
                     {
                         if (ex != null)
                         {                       
                             //This is where I'm having issues
                             //If an error occurs I want to switch back to the Login tab which will enable the user to try Login again
                             //This does not throw an error but it also doesn't show the Login tab
                             SetActiveTab(TabType.Login);
                         }
                         else
                         {
                            //No error perform additional processing
                         }
                     });
    }

    private void SetActiveTab(TabType type)
    {
        //If I leave the code as simply:
        //loaderTabs.SelectedIndex = (int)type;
        //I get an error when seting the tab for the second time:
        //Invalid Operation Exception (The calling thread cannot access this object because a different thread owns it.) 

        loaderTabs.Dispatcher.Invoke((Action)(() =>
        {
            loaderTabs.SelectedIndex = (int)type;
        }));
    }
}
4

2 に答える 2

1

私は WPF の専門家ではありませんが、なぜこの機能にディスパッチャー オブジェクトを使用するのでしょうか?

private void SetActiveTab(TabType type) 
{
  loaderTabs.SelectedIndex = (int)type; 
}

編集:

わかりました、なぜディスパッチャを使用するのかがよくわかりました。別のスレッドで処理中にコードのビットを試してみましたが、うまくいきました。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
using System.ComponentModel;


namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
public partial class MainWindow : Window
{

    private BackgroundWorker _worker;


    public MainWindow()
    {
        InitializeComponent();
        _worker = new BackgroundWorker();
        _worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
        _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_worker_RunWorkerCompleted);

    }

    void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        MessageBox.Show("Done");
    }

    void _worker_DoWork(object sender, DoWorkEventArgs e)
    {
        Thread.Sleep(5000);
    }

    private void SetActiveTab(TabType type)
    {
        loaderTabs.Dispatcher.Invoke((Action)(() =>
        {
            //This is where the error happens when I try set the active tab back to tab1 
            loaderTabs.SelectedIndex = (int)type;
        }));
    }


    public void Login(string userName, string password)
    {
        try
            {
               SetActiveTab(TabType.Loader);
               //Processing... 
               _worker.RunWorkerAsync();
           }
           catch (Exception)
           {
               SetActiveTab(TabType.Login);
           }
       }

        enum TabType {Login, Loader};

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Login("user", "password");
        }

   }
}
于 2012-08-29T20:40:35.107 に答える
0

なぜタブ コントロールを使用しているのですか? 不要です。

を使用してこれを実装し、 を使用してプロパティをGrid切り替えます。VisibilityIValueConverter

<Grid>
   <Grid x:Name="Login" Visibility="{Binding Path=IsProcessing, Converter={StaticResource InvertBooleanToVisilityConverter}}">
       <!-- Your login fields -->
   </Grid>
   <Grid x:Name="Status" Visibility="{Binding Path=IsProcessing, Converter={StaticResource BooleanToVisilityConverter}}">
       <!-- Your login status -->
   </Grid>
</Grid>

IsProcessingプロパティが変更されたことを通知する単純なブール値のプロパティであり、2つIValueConvertersは単にこのブール値を同等のVisibility値に変換しています。

私は常にこのパターンを使用して、複数の状態を表示できる複雑なインターフェイスを作成しています (EnumToBoolコンバーターを でチェーンすることでこれを実現していますBoolToVisibility)。


また、スレッドの所有権とその考えられる原因に関する質問に直接答えるには。別のスレッドから を 2 回目に呼び出している可能性がありますSetActiveTab(たとえば、最初のタブに戻るため)。プロパティを変更する前に、メイン スレッド (コントロールを作成したスレッド) を呼び出す必要があります。これを行う方法については、StackOverflow と Google で既に多くの情報が公開されています。私が遭遇したほぼすべてのケースで、例外がスローされた行は問題のある場所ではなく、コールスタックに戻る必要があります。

于 2012-08-29T21:54:57.097 に答える