Написать один класс диалога. Это подкласс Window. Он имеет XAML:
<Window
...blah blah blah...
Title="{Binding Title}"
>
<StackPanel MinWidth="300">
<!-- This is how you place content within content in WPF -->
<ContentControl
Content="{Binding}"
Margin="2"
/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="2,20,2,2">
<Button
Margin="2"
MinWidth="60"
DockPanel.Dock="Right"
Content="OK"
Click="OK_Click"
IsDefault="True"
/>
<Button
Margin="2"
MinWidth="60"
DockPanel.Dock="Right"
Content="Cancel"
IsCancel="True"
Click="Cancel_Click"
/>
</StackPanel>
</StackPanel>
</Window>
Вы можете придумать это бесконечно, но это достойный минимум, чтобы дать вам произвольный контент над рядом выровненных по правому краю кнопок. Добавление дополнительных кнопок по мере необходимости может включать либо создание шаблонов для этой части окна, либо создание их с помощью ItemsControl (я сделал это в нашем рабочем коде) или несколько других опций.
Использование:
var vm = new SomeDialogViewModel();
var dlg = new MyDialog { DataContext = vm };
Для каждой модели представления диалога потребители должны определить неявную матрицу данных , которая предоставляет пользовательский интерфейс для этой модели представления.
Я бы предложил написать диалоговый интерфейс модели представления, который, как ожидается, будет реализован потребителем.
public interface IDialogViewModel
{
String Title { get; set; }
void OnOK();
// Let them "cancel the cancel" if they like.
bool OnCancel();
}
Окно может проверить, реализует ли его DataContext этот интерфейс, и действовать соответствующим образом. Если хотите, он может потребовать , чтобы интерфейс и выбрасывать исключение не было реализовано, или он мог бы просто общаться с ним, только если он там есть. Если они не реализуют его, но у них все еще есть свойство Title
, привязка к Title
все равно будет работать. Привязки "утки".
Естественно, вы можете написать OKCancelDialogViewModel
или SelectStringFromListViewModel
, написать соответствующие DataTemplates, которые реализуют свои пользовательские интерфейсы, и написать хорошие чистые статические методы, которые показывают их:
public static class Dialogs
{
public static TOption Select<TOption>(IEnumerable<TOption> options, string prompt,
string title = "Select Option") where TOption : class
{
// Viewmodel isn't generic because that breaks implicit datatemplating.
// That's OK because XAML uses duck typing anyhow.
var vm = new SelectOptionDialogViewModel
{
Title = title,
Prompt = prompt,
Options = options
};
if ((bool)new Dialog { DataContext = vm }.ShowDialog())
{
return vm.SelectedOption as TOption;
}
return null;
}
// We have to call the value-type overload by a different name because overloads can't be
// distinguished when the only distinction is a type constraint.
public static TOption? SelectValue<TOption>(IEnumerable<TOption> options, string prompt,
string title = "Select Option") where TOption : struct
{
var vm = new SelectOptionDialogViewModel
{
Title = title,
Prompt = prompt,
// Need to box these explicitly
Options = options.Select(opt => (object)opt)
};
if ((bool)new Dialog { DataContext = vm }.ShowDialog())
{
return (TOption)vm.SelectedOption;
}
return null;
}
}
Воттаблица данных модели представления для вышеприведенного диалогового окна выбора:
<Application.Resources>
<DataTemplate DataType="{x:Type local:SelectOptionDialogViewModel}">
<StackPanel>
<TextBlock
TextWrapping="WrapWithOverflow"
Text="{Binding Prompt}"
/>
<ListBox
ItemsSource="{Binding Options}"
SelectedItem="{Binding SelectedOption}"
MouseDoubleClick="ListBox_MouseDoubleClick"
/>
</StackPanel>
</DataTemplate>
</Application.Resources>
App.xaml.cs
private void ListBox_MouseDoubleClick(object sender,
System.Windows.Input.MouseButtonEventArgs e)
{
((sender as FrameworkElement).DataContext as IDialogViewModel).DialogResult = true;
}
var a = Dialogs.Select(new String[] { "Bob", "Fred", "Ginger", "Mary Anne" },
"Select a dance partner:");
var b = Dialogs.SelectValue(Enum.GetValues(typeof(Options)).Cast<Options>(),
"Select an enum value:");