カスタム ComboBox の動作を記述
[TemplatePart(Name = CustomComboBox.PART_FAVICONBUTTON, Type = typeof(Button))] [TemplatePart(Name = CustomComboBox.PART_DROPDOWNBUTTON, Type = typeof(ToggleButton))] [TemplatePart(Name = CustomComboBox.PART_TEXTBOX, Type = typeof(TextBox))] [TemplatePart(Name = CustomComboBox.PART_POPUP, Type = typeof(Popup))] [TemplatePart(Name = CustomComboBox.PART_LISTBOX, Type = typeof(ListBox))] public class CustomComboBox : ComboBox { internal const string PART_TEXTBOX = "PART_TextBox"; internal const string PART_DROPDOWNBUTTON = "PART_DropDownButton"; internal const string PART_FAVICONBUTTON = "PART_FaviconButton"; internal const string PART_POPUP = "PART_Popup"; internal const string PART_LISTBOX = "PART_ListBox"; private Popup _popup; private Button _faviconButton; public TextBox EditBox { get; private set; } public event RoutedEventHandler FaviconButtonClick; static CustomComboBox() { DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomComboBox), new FrameworkPropertyMetadata(typeof(CustomComboBox))); } public override void OnApplyTemplate() { base.OnApplyTemplate(); _popup = GetTemplateChild(PART_POPUP) as Popup; if (_popup != null) { _popup.Opened += (sender, e) => { // Popup の幅を揃える _popup.Width = ActualWidth; }; } _faviconButton = GetTemplateChild(PART_FAVICONBUTTON) as Button; if (_faviconButton != null) { _faviconButton.Click += (sender, e) => { var handler = FaviconButtonClick; if (handler != null) { handler(this, new RoutedEventArgs()); } }; } EditBox = GetTemplateChild(PART_TEXTBOX) as TextBox; } }
外部から ComboBox 内の TextBox にアクセスするためのプロパティを追加しています。ComboBox の Text プロパティから入力された文字列を取得できなかった為、苦肉の策です。
LocationBar の動作を記述
[TemplatePart(Name=LocationBar.PART_COMBOBOX,Type=typeof(CustomComboBox))] [TemplatePart(Name = LocationBar.PART_GOBUTTON, Type = typeof(Button))] public class LocationBar : Control { internal const string PART_GOBUTTON = "PART_GoButton"; internal const string PART_COMBOBOX = "PART_ComboBox"; private CustomComboBox _comboBox; private Button _goButton; public event EventHandler<RequestNavigateEventArgs> RequestNavigate; public event EventHandler<RequestBookmarkEventArgs> RequestBookmark; static LocationBar() { DefaultStyleKeyProperty.OverrideMetadata(typeof(LocationBar), new FrameworkPropertyMetadata(typeof(LocationBar))); } public override void OnApplyTemplate() { base.OnApplyTemplate(); _comboBox = GetTemplateChild(PART_COMBOBOX) as CustomComboBox; if (_comboBox != null) { _comboBox.PreviewKeyDown += new KeyEventHandler(_comboBox_PreviewKeyDown); _comboBox.FaviconButtonClick += new RoutedEventHandler(_comboBox_FaviconButtonClick); } _goButton = GetTemplateChild(PART_GOBUTTON) as Button; if (_goButton != null) { _goButton.Click += new RoutedEventHandler(_goButton_Click); } } // ファビコンボタンをクリックされたとき private void _comboBox_FaviconButtonClick(object sender, RoutedEventArgs e) { string uri = _comboBox.EditBox.Text; if (!string.IsNullOrEmpty(uri)) { OnRequestBookmark(new RequestBookmarkEventArgs(uri)); } } // 移動ボタンをクリックされたとき private void _goButton_Click(object sender, RoutedEventArgs e) { string uri = _comboBox.EditBox.Text; if (!string.IsNullOrEmpty(uri)) { OnRequestNavigate(new RequestNavigateEventArgs(uri)); } } private void _comboBox_PreviewKeyDown(object sender, KeyEventArgs e) { // 入力エリアで Enter を押されたら ComboBox に URL 履歴を追加して // イベントを発生させる if (e.Key == Key.Enter) { string uri = _comboBox.EditBox.Text; if (!string.IsNullOrEmpty(uri)) { // ComboBox に履歴を残す if (_comboBox.Items.Contains(uri)) { _comboBox.Items.Remove(uri); } _comboBox.Items.Insert(0, uri); _comboBox.SelectedIndex = 0; OnRequestNavigate(new RequestNavigateEventArgs(uri)); } } } protected virtual void OnRequestNavigate(RequestNavigateEventArgs e) { var handler = RequestNavigate; if (handler != null) { handler(this, e); } } protected virtual void OnRequestBookmark(RequestBookmarkEventArgs e) { var handler = RequestBookmark; if (handler != null) { handler(this, e); } } } public class RequestNavigateEventArgs : EventArgs { public string Uri { get; private set; } public RequestNavigateEventArgs(string uri) { Uri = uri; } } public class RequestBookmarkEventArgs : EventArgs { public string Uri { get; private set; } public RequestBookmarkEventArgs(string uri) { Uri = uri; } }
まとめ
今回作成したローケーションバーは、以前作成した検索バーと、作成手順はほとんど変わっていません。入力系のコントロールを作る場合、たいていは同じ手順になると思います。今回の作成手順は次の通りです。
- Border の中に Grid や StackPanel などを使ってコントロール要素を配置。
- 各要素に適用するスタイルを記述。必要ならテンプレートも記述する。
- コントロール本体のスタイル(テンプレート含む)を記述する。
- コントロールの動作を記述する。イベントやプロパティを追加したり、コントロール間のプロパティやイベントを結びつけたりする。
あと、「WPF でコイツを再現してほしい」っていうリクエストがあれば受け付けます。というか是非。ネタが切れそうなので(;^_^A