性格いい人が優勝

visual C# 2010 Express を使用しています。

例えばWPFで新しいプロジェクトを立ち上げるとMainWindow.xamlがあって
そのウインドウの中にはGridが内装されていると思います。
そこで、UserControl1.xamlとUserControl2.xamlを追加して
最初はMainWindowのGridにはUserControl1を貼っておきます。
そしてMainWindowのボタンかなにかをクリックしたら
プログラムで動的にUserControl2に貼り替えたいのですが、
MainWindowクラス内でthis.(ここでプロパティやメンバを)候補を探しても
gridなどが見つからないのです・・・
どうやったら(なにで)動的にこのような切り替えをすることができるのでしょうか?

A 回答 (4件)

VSのみだとVSMは使いにくい点も多いです。


なので,VSのみであるならば,コンバーター使って処理する方がお薦めになります。


> すいません、それとGridにはUserControl1が乗っているはずなのですが、
> プロパティやイベントを見てもUserControl1というのがないのですが(あればそこを切り替えれる)
> GridのどこにUserControl1はあるのでしょうか?

UserControlに名前をつけて下さい。
その名前で,Windowのフィールドとしてオブジェクトが存在します。


> クリックしたときに、ただユーザーコントロールの表示の切り替えをするのではなくて
> データベースを使った認証処理などプログラム的な処理も行いたいので、
> やはりなにかとコードから扱えた方が良いかと思うのですが、
> そこらへんもふくめて教えていただけないでしょうか?

このくらいだと,認証用ダイアログでMessangerやそれに似た機構を用意する必要があるくらいで,
あとはコードでUIを触る必要はなさそうです。

WinForms時代から,.NET FrameworkにはData Bindingという仕組みがあります。
さらに,WPFではCommand BindingやConverterといった仕組みが加わりました。
これらの仕組みによって,コードからUIを触る必要はそれほどなくなります。

例えば,
namespace WpfApplication1
{
public class Foo : System.ComponentModel.INotifyPropertyChanged
{
private bool _authorized = false;
private UserControl1ViewModel _uc1Model;
private UserControl2ViewModel _uc2Model;

public Foo ()
{
_uc1Model = new UserControl1ViewModel(this);
_uc2Model = new UserControl2ViewModel(this);
}

public bool Authorized
{
get { return _authorized; }
set
{
_authorized = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("Authorized"));
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("NotAuthorized"));
}
}
}
public bool NotAuthorized { get { return !Authorized; } }
public UserControl1ViewModel ModelForUserControl1 { get { return _uc1Model; } }
public UserControl2ViewModel ModelForUserControl2 { get { return _uc2Model; } }
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
}
}
なんてクラスがViewModelとしてあったとして,
<Window xmlns:local="clr-namespace:WpfApplication1">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter1" />
</Window.Resources>
<Window.DataContext>
<local:Foo/>
</Window.DataContext>
<Grid>
<local:UserControl1 Visibility="{Binding Path=NotAuthorized, Converter={StaticResource BooleanToVisibilityConverter1}}" DataContext="{Binding ModelForUserControl1}" />
<local:UserControl2 Visibility="{Binding Path=Authorized, Converter={StaticResource BooleanToVisibilityConverter1}}" DataContext="{Binding ModelForUserControl2}" />
</Grid>
</Window>
のような記述をすれば,Foo.Authorizedの値の設定によってUserControl1とUserControl2の表示を切り替えられます。
# XAMLはだいぶ手抜きをしています。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
ユーザーコントロールに「x:Name="uc1" 」と付け加えたのですが
今回は見つけることができませんでした・・・一応ビルドもしてます。
コード的には
MainWindow.xaml.csのクラス内で
thisやthis.grid1から探してるのですが、見つかりません。。
VS的(?)には
フォームのプロパティがありますよね?
それのプロパティ、イベント両方でuc1というのがないのです・・・
具体的にどこを探せば良いのでしょうか?

それとすいません、具体的なコード示して頂きありがとうございます。
なんとなくこのプログラムのやりたいことは理解できるのですが、
つまりFoo.Authorizedの値の設定はボタンかなにかを用意して
それをクリックしたときに例えばloginButton_Click内で
Foo.Authorizedの値を設定すれば良いのでしょうか?
それともその値の設定すらなにかxaml側で自動で行ってくれるのでしょうか?

お礼日時:2011/10/11 14:02

> で始めからuserControl11があったわけです。


とのことなので,
> <local:UserControl1 x:Name="uc1" ...
> は具体的にどこに記述すれば良いのでしょうか?
は不要です。


> ただ自分としてはgrid1の内容を切り替えたいので、
> this.grid1.(ここに例えばcontentsとか) = userControl12(新しいコントロール);
> のような感じでコード上で設定したいのですが、
> this.grid1のどのプロパティやメンバを探せば良いのでしょうか?

考え方を変えてみると良いと思います。
私の今までの回答の方針でもありますが,
「コントロールの表示状態を切り替える」ことができれば表示内容が切り替わります。

つまり,
userControl2.Visibility = Visibility.Visible;
userControl1.Visibliity = Visibility.Collapse;
とやれば,表示が切り替わります。
わざわざコントロールを動的に生成してGridに追加,とかする必要はありません。
# ここでもConverterとUIElement間でのBinding使うと,片方だけで済ますことは可能。


一応,
grid1.Children.Add(new UserControl2(...));
みたいにすれば,動的にコントロールを追加することは可能です。
ただ,その後に,結局コントロールの初期設定が必要なわけで,
それをするくらいならVisibilityの制御で済ます方が簡単だと思います。
    • good
    • 1
この回答へのお礼

ご回答ありがとうございます。
なるほど、Visibility を使って切り替えができました。
参考になりました。
あとはYune-Kichiさんが提示して頂いたコードを元に
自分なりに作ってみたいと思います。
コードも含めてとても参考になりました。
ありがとうございます。

お礼日時:2011/10/12 10:49

> ユーザーコントロールに「x:Name="uc1" 」と付け加えたのですが


> 今回は見つけることができませんでした・・・一応ビルドもしてます。

ユーザーコントロールのインスタンスに名前をつけていますか?
先の例だと,
<local:UserControl1 x:Name="uc1" ...
のように書く必要あります。


> なんとなくこのプログラムのやりたいことは理解できるのですが、
> つまりFoo.Authorizedの値の設定はボタンかなにかを用意して
> それをクリックしたときに例えばloginButton_Click内で
> Foo.Authorizedの値を設定すれば良いのでしょうか?

そうなります。

ちなみに,突き詰めていくとButtonのClickイベントはICommand派生クラスをバインドさせることによって処理を委譲できるため,
ICommand.Executeの実装中でAutorizedの値を変更することになります。
で,RelayCommandなどの名前で呼ばれるICommand実装クラスは,デリゲートを指定することで自由にメソッドを指定できるようにします。
# RelayCommandとかDelegateCommandで探すと,似たような実装が山のように見つかると思います。
これらを使うと,UserControl1にバインドされたクラスのメソッドに実行をほぼ完全に委譲できます。

WPF/Silverlightでは,バインド機構を最大限活用する方が作りやすくなりますよ。
DataTemplateなど,バインド機構を前提としたものがたくさん存在します。
# 本質的には,表示自体のためだけのデータや動作以外を分離して,テスト等をやりやすくする為のものです。

WPF/Silverlightの標準的なパターンとなってきているMVVMというパターンも,
この強力なバインド機構が前提にあります。

example) MVVMもどき(末端のMがなかったり色々と……)
namespace WpfApplication1
{
using System.Windows.Input;
using System.ComponentModel;

public sealed class RelayCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;

public RealyCommand (Action execute, Func<bool> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public void Execute (object parameter) // そのコマンドを実行する
{
if (_execute != null) _execute();
}
public bool CanExecute (object parameter) // そのコマンドが実行可能かどうかの判定
{
// _executeが指定されていないなら常に実行不可
// _canExecuteが指定されていないなら常に実行可能
// どちらも指定されているなら_canExecuteに委譲
return _execute != null && (_canExecute == null || _canExecute());
}
public event EventHandler CanExecuteChanged {
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}

public class UserControl1ViewModel : INotifyPropertyChanged
{
private string _loginName;
private string _password;
private readonly ICommand _login;
private readonly Foo _parent;

public UserControl1ViewModel () : this(null) { }
public UserControl1ViewModel (Foo parent)
{
_loginName = "";
_password = "";
_login = new RelayCommand(LoginImpl, CanLogin); // ログインボタンはCanLoginメソッドで処理自体の可否,LoginImplで実際の処理
_parent = parent;
}

// ログイン名
public string LoginName
{
get { return _loginName; }
set
{
_loginName = value ?? "";
if (PropertyChanged != null) PropertyChanged(new PropertyChangedEventArgs("LoginName"));
CommandManager.InvalidateRequerySuggested();
}
}

// パスワード
// System.Windows.Controls.PasswordBox.PasswordやSecurePasswordはDependencyPropertyではないのでバインド不可
// PasswordChangedイベントにトリガを仕掛けるか,素直にPasswordChangedイベントを処理する
public string Password
{
get { return _password; }
set
{
_password = value ?? "";
if (PropertyChanged != null) PropertyChanged(new PropertyChangedEventArgs("Password"));
CommandManager.InvalidateRequerySuggested();
}
}

// 以下,ログイン処理用
public ICommand Login { get{ return _login; } }
private void LoginImpl ()
{
// 実際のログイン処理 (ここでは省略)
_parent.Authorized = _loginName == "user" && _password == "pass";
}
private bool CanLogin ()
{
// ログインチェックへの条件
return _parent != null && !String.IsNullOrWhiteSpace(_loginName) && !String.IsNullOrWhiteSpace(_password);
}
}
}

// UserControl1.xaml
<UserControl xmlns:local="clr-namespace:WpfApplication1" d:DataContext="{x:Type local:UserControl1ViewModel}">
<Grid>
<Grid.RowDefinitions>
<Grid.RowDefinition />
<Grid.RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<Grid.ColumnDefinition />
<Grid.ColumnDefinition Width="auto" />
<Grid.ColumnDefinitions />
<TextBox Text="{Binding Path=LoginName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Text="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" /> <!-- 面倒なのでPasswordBoxは使わない -->
<Button Content="ログイン" Command="{Binding Path=Login}" Grid.Column="1" Grid.RowSpan="2" />
</Grid>
</UserControl>
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
なるほど、このプログラムもいいですね。
これから作る上で参考にさせてもらいます。

それとすいません、No2で
>thisやthis.grid1から探してるのですが、見つかりません。。
と言ったのですが、もともとthisからはuserControl11がありました・・・
this.userControl11
です。つまりMainWindow.xamlの中身は

<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/pre …
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" xmlns:my="clr-namespace:WpfApplication1">
<Grid x:Name="grid1">
<my:UserControl1 HorizontalAlignment="Left" Margin="188,78,0,0" x:Name="userControl11" VerticalAlignment="Top" Height="139" Width="184" />
</Grid>
</Window>

で始めからuserControl11があったわけです。
ただ自分としてはgrid1の内容を切り替えたいので、

this.grid1.(ここに例えばcontentsとか) = userControl12(新しいコントロール);

のような感じでコード上で設定したいのですが、
this.grid1のどのプロパティやメンバを探せば良いのでしょうか?
Yune-Kichiさんの仰る
<local:UserControl1 x:Name="uc1" ...
は具体的にどこに記述すれば良いのでしょうか?

お礼日時:2011/10/11 22:59

Gridに名前をつけましたか。


<Grid x:Name="foo">
のように,名前をつけないと,コードから名前で扱うことは出来ません。

ただ,この場合だと,私の場合はVisualStateManagerで切り替えるか,
Boolean→VisibilityのValueConverter作ってそれぞれのVisiblityにバインドさせるなどによって,
表示に関してはコードから触らないようにしますが。

この回答への補足

すいません、それとGridにはUserControl1が乗っているはずなのですが、
プロパティやイベントを見てもUserControl1というのがないのですが(あればそこを切り替えれる)
GridのどこにUserControl1はあるのでしょうか?

補足日時:2011/10/11 01:23
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。
Gridの名前ですが、「x:」が抜けていたので付け加えたらコードで扱えました。
>VisualStateManagerで切り替えるか,
これはどのようにやるのでしょうか?

http://blogs.yahoo.co.jp/elku_simple/24926044.html

ここを参考に見てみたのですが、自分としては
クリックしたときに、ただユーザーコントロールの表示の切り替えをするのではなくて
データベースを使った認証処理などプログラム的な処理も行いたいので、
やはりなにかとコードから扱えた方が良いかと思うのですが、
そこらへんもふくめて教えていただけないでしょうか?

お礼日時:2011/10/11 01:15

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!