- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我有一个可重复使用的对话框/窗口,提示用户从列表框中选择一个项目,点击“确定”以确认选择。
效果很好;然而,列表框并不知道它提前处理的是什么类型的数据。因此,该列表绑定(bind)到 ObservableCollection<Object>
。可以由对话框的调用者设置。
此外,列表框有一个自定义项目模板,允许用户从列表中删除项目。
这是我描述的对话框:
理想情况下,我想利用 DisplayMemberPath
对于列表框,但我不允许,因为我正在创建自己的项目模板。这是一个问题,因为调用者应该能够指定他/她想要绑定(bind)到我设置的自定义项目模板的属性。
由于这种方法行不通,我的第一个问题是:
在 XAML 中,我希望看到类似这样的内容,但这是错误的:
<ListBox.ItemTemplate>
<Label Content="{Binding Path={Binding CustomPath}}"/>
<Button Width="20" Height="20" FontWeight="Bold" Content="×"/>
</ListBox.ItemTemplate>
(为简洁起见省略了一些属性)
假设第一个问题解决了,我还剩下另一个问题。列表框使用非泛型类型 Object
它将没有调用者想要绑定(bind)的属性。列表框无法将对象转换为自定义类型并访问所需的属性。这引出了我的第二个问题。
也许这应该留给关于 SO 的另一个问题,但是能够指定绑定(bind)是否使用 ToString()
会很好或属性(property)。
我能想到的唯一解决方案是创建一个接口(interface),该接口(interface)具有调用者必须使用的属性(名为 DisplayText
)。然后该列表将绑定(bind)到 ObservableCollection<CustomInterface>
的一个实例.
但是,不希望将已经存在的数据类型包装到此接口(interface)中以使其起作用。有更好的方法吗?
编辑:实现者如何使用 ListDialogBox
以下是我希望调用者能够设置对话框的方式(或类似简单的设置):
public CustomItem PromptForSelection()
{
ListDialogBox dialog = new ListDialogBox();
dialog.Items = GetObservableCollection();
dialog.ListDisplayMemberPath = "DisplayName";
dialog.ShowDialog();
if(!dialog.IsCancelled)
{
return (CustomItem) dialog.SelectedItem;
}
}
public ObservableCollection<Object> GetObservableCollection()
{
ObservableCollection<Object> coll = new ObservableCollection<Object>();
CustomItem item = new CustomItem();
item.DisplayName = "Item1";
CustomItem item2 = new CustomerItem();
item2.DisplayName = "Item2";
//...
coll.Add(item);
coll.Add(item2);
//...
return coll;
}
代码将无法工作,因为如果 ObservableCollection<Object>
则 DisplayName 属性没有意义用于 ListDialogBox
.这是因为 Object
没有定义该属性。
在ListDialogBox
类,我想将项目模板的标签绑定(bind)到 DisplayName 属性,因为那是 ListDisplayMemberPath
提供的值(value)。
我怎样才能克服这个问题?
最佳答案
此答案旨在解决原始问题中的问题,并提供如何实现 ListDialogBox
的示例。供 future 的读者使用。
原始问题中的问题涉及能够指定如何在 ListBox
中显示信息。 .自 ListBox
直到运行时才知道它显示的是什么类型的数据,因此没有一种直接的方法来指定指向要显示的所需属性的“路径”。
这个问题最简单的解决方案是创建一个接口(interface),ListDialogBox
独占使用,然后调用者只需要创建该接口(interface)的实例来自定义信息的显示方式。
此解决方案的唯一缺点是调用者需要满足他/她的数据以符合 ListDialogBox
;然而,这很容易实现。
ListDialogBox
:ListDialogBox
的目标类似于 OpenFileDialog
或 SaveFileDialog
因为您初始化对话框,提示输入结果,然后处理结果。
首先,我将展示并解释 ListDialogBox
的代码(XAML 和代码隐藏)。
下面的 XAML 已被修剪以仅显示对话框的结构和必要的属性。
<Window
//You must specify the namespace that contains the the converters used by
//this dialog
xmlns:local="clr-namespace:<your-namespace>"
//[Optional]: Specify a handler so that the ESC key closes the dialog.
KeyDown="Window_KeyDown">
<Window.Resources>
//These converters are used to control the dialog box.
<BooleanToVisibilityConverter x:Key="BoolToVisibility"/>
<local:NullToBooleanConverter x:Key="NullToBool"/>
</Window.Resources>
<Grid>
//This displays a custom prompt which can be set by the caller.
<TextBlock Text="{Binding Prompt}" TextWrapping="Wrap" />
//The selection button is only enabled if a selection is made (non-null)
<Button IsEnabled="{Binding Path=SelectedItem,
ElementName=LstItems,
Converter={StaticResource NullToBool}}"
//Display a custom message for the select button.
Content="{Binding SelectText}"
//Specify a handler to close the dialog when a selection is confirmed.
Click="BtnSelect_Click" Name="BtnSelect" />
//The cancel button specifies a handler to close the dialog.
<Button Content=" Cancel" Name="BtnCancel" Click="BtnCancel_Click" />
//This list box displays the items by using the 'INamedItem' interface
<ListBox ItemsSource="{Binding Items}" Name="LstItems"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<Button DockPanel.Dock="Right"
//The delete button is only available when the 'CanRemoveItems'
//property is true. See usage for more details.
Visibility="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
Path=CanRemoveItems,
Converter={StaticResource BoolToVisibility}}"
//Visual properties for correctly displaying the red 'x'.
//The 'x' is actually the multiplication symbol: '×'
FontFamily="Elephant" Foreground="Red" FontWeight="Bold" FontStyle="Normal"
FontSize="18" Padding="0,-3,0,0" Content="×"
//[Optional]: Align button on the right end.
HorizontalAlignment="Right"
//Specify handler that removes the item from the list (internally)
Click="BtnRemove_Click" />
//The DockPanel's last child fills the remainder of the template
//with the one and only property from the INamedItem interface.
<Label Content="{Binding DisplayName}"
//[Optional]: This handler allows double-clicks to confirm selection.
MouseDoubleClick="LstItem_MouseDoubleClick"/>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
NullToBooleanConverter
本质上与 this answer 相同在 SO 上找到。它用于根据ListBox.SelectedItem
是否启用/禁用确认选择按钮。是null
.这个转换器的不同之处在于它返回 true
当转换后的值为NOT null
.
ListDialogBox
代码隐藏:这个类定义了调用者可以修改以自定义方式的所有属性ListDialogBox
显示及其具有的功能。
public partial class ListDialogBox : Window, INotifyPropertyChanged
{
/* The DataContext of the ListDialogBox is itself. It implements
* INotifyPropertyChanged so that the dialog box bindings are updated when
* the caller modifies the functionality.
*/
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
/* Optionally, the ListDialogBox provides a callback mechanism that allows
* the caller to cancel the removal of any of the items.
* See usage for more details.
*/
public event RemoveItemEventHandler RemoveItem;
protected void RaiseRemoveItem(RemoveItemEventArgs args)
{
if (RemoveItem != null)
{
RemoveItem(this, args);
}
}
//Local copies of all the properties. (with default values)
private string prompt = "Select an item from the list.";
private string selectText = "Select";
private bool canRemoveItems = false;
private ObservableCollection<INamedItem> items;
private INamedItem selectedItem = null;
public ListDialogBox()
{
InitializeComponent();
DataContext = this; //The DataContext is itself.
}
/* Handles when an item is double-clicked.
* The ListDialogBox.SelectedItem property is set and the dialog is closed.
*/
private void LstItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
SelectedItem = ((FrameworkElement)sender).DataContext as INamedItem;
Close();
}
/* Handles when the confirm selection button is pressed.
* The ListDialogBox.SelectedItem property is set and the dialog is closed.
*/
private void BtnSelect_Click(object sender, RoutedEventArgs e)
{
SelectedItem = LstItems.SelectedItem as INamedItem;
Close();
}
/* Handles when the cancel button is pressed.
* The lsitDialogBox.SelectedItem remains null, and the dialog is closed.
*/
private void BtnCancel_Click(object sender, RoutedEventArgs e)
{
Close();
}
/* Handles when any key is pressed. Here we determine when the user presses
* the ESC key. If that happens, the result is the same as cancelling.
*/
private void Window_KeyDown(object sender, KeyEventArgs e)
{ //If the user presses escape, close this window.
if (e.Key == Key.Escape)
{
Close();
}
}
/* Handles when the 'x' button is pressed on any of the items.
* The item in question is found and the RemoveItem event subscribers are notified.
* If the subscribers do not cancel the event, then the item is removed.
*/
private void BtnRemove_Click(object sender, RoutedEventArgs e)
{ //Obtain the item that corresponds to the remove button that was clicked.
INamedItem removeItem = ((FrameworkElement)sender).DataContext as INamedItem;
RemoveItemEventArgs args = new RemoveItemEventArgs(removeItem);
RaiseRemoveItem(args);
if (!args.Cancel)
{ //If not cancelled, then remove the item.
items.Remove(removeItem);
}
}
//Below are the customizable properties.
/* This property specifies the prompt that displays at the top of the dialog. */
public string Prompt
{
get { return prompt; }
set
{
if (prompt != value)
{
prompt = value;
RaisePropertyChanged("Prompt");
}
}
}
/* This property specifies the text on the confirm selection button. */
public string SelectText
{
get { return selectText; }
set
{
if (selectText != value)
{
selectText = value;
RaisePropertyChanged("SelectText");
}
}
}
/* This property controls whether or not items can be removed.
* If set to true, the the 'x' button appears on the ItemTemplate.
*/
public bool CanRemoveItems
{
get { return canRemoveItems; }
set
{
if (canRemoveItems != value)
{
canRemoveItems = value;
RaisePropertyChanged("CanRemoveItems");
}
}
}
/* This property specifies the collection of items that the user can select from.
* Note that this uses the INamedItem interface. The caller must comply with that
* interface in order to use the ListDialogBox.
*/
public ObservableCollection<INamedItem> Items
{
get { return items; }
set
{
items = value;
RaisePropertyChanged("Items");
}
}
//Below are the read only properties that the caller uses after
//prompting for a selection.
/* This property contains either the selected INamedItem, or null if
* no selection is made.
*/
public INamedItem SelectedItem
{
get { return selectedItem; }
private set
{
selectedItem = value;
}
}
/* This property indicates if a selection was made.
* The caller should check this property before trying to use the selected item.
*/
public bool IsCancelled
{ //A simple null-check is performed (the caller can do this too).
get { return (SelectedItem == null); }
}
}
//This delegate defines the callback signature for the RemoveItem event.
public delegate void RemoveItemEventHandler(object sender, RemoveItemEventArgs e);
/* This class defines the event arguments for the RemoveItem event.
* It provides access to the item being removed and allows the event to be cancelled.
*/
public class RemoveItemEventArgs
{
public RemoveItemEventArgs(INamedItem item)
{
RemoveItem = item;
}
public INamedItem RemoveItem { get; private set; }
public bool Cancel { get; set; }
}
INamedItem
界面:现在ListDialogBox
已经呈现,我们需要看看调用者如何使用它。如前所述,最简单的方法是创建一个接口(interface)。
INamedItem
接口(interface)只提供一个属性(称为 DisplayName
)和 ListDialogBox
需要这些的列表才能显示信息。 ListDialogBox
取决于调用者为此属性设置一个有意义的值。
界面非常简单:
public interface INamedItem
{
string DisplayName { get; set; }
}
此时,所有与 ListDialogBox
的功能相关的类已经介绍过了,现在是时候在程序中查看和实现它了。
为此,我们需要实例化 ListDialogBox
,然后设置自定义任何所需的属性。
ListDialogBox dialog = new ListDialogBox();
dialog.Prompt = "Select a pizza topping to add from the list below:";
dialog.SelectText = "Choose Topping";
dialog.CanRemoveItems = true; //Setting to false will hide the 'x' buttons.
ListDialogBox
需要 ObservableCollection<INamedItem>
,所以我们必须在继续之前生成它。为此,我们为要使用的数据类型创建了一个“包装器类”。在此示例中,我将创建一个 StringItem
实现 INamedItem
的类并设置 DisplayName
到任意字符串。见下文:
public class StringItem : INamedItem
{ //Local copy of the string.
private string displayName;
//Creates a new StringItem with the value provided.
public StringItem(string displayName)
{ //Sets the display name to the passed-in string.
this.displayName = displayName;
}
public string DisplayName
{ //Implement the property. The implementer doesn't need
//to provide an implementation for setting the property.
get { return displayName; }
set { }
}
}
StringItem
然后用于创建 ObservableCollection<INamedItem>
:
ObservableCollection<INamedItem> toppings = new ObservableCollection<INamedItem>();
toppings.Add(new StringItem("Pepperoni"));
toppings.Add(new StringItem("Ham"));
toppings.Add(new StringItem("Sausage"));
toppings.Add(new StringItem("Chicken"));
toppings.Add(new StringItem("Mushroom"));
toppings.Add(new StringItem("Onions"));
toppings.Add(new StringItem("Olives"));
toppings.Add(new StringItem("Bell Pepper"));
toppings.Add(new StringItem("Pineapple"));
//Now we can set the list property:
dialog.Items = toppings;
至此基本实现已经设置完毕。我们只需要调用dialog.ShowDialog()
, 并处理结果。但是,由于示例允许用户从列表中删除项目,我们可能需要提示进行确认。为此,我们需要订阅 RemoveItem
事件。
RemoveItemEventHandler myHandler = (object s, RemoveItemEventArgs args) =>
{
StringItem item = args.RemoveItem as StringItem;
MessageBoxResult result = MessageBox.Show("Are you sure that you would like" +
" to permanently remove \"" + item.DisplayName + "\" from the list?",
"Remove Topping?",
MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.No)
{ //The user cancelled the deletion, so cancel the event as well.
args.Cancel = true;
}
};
//Subscribe to RemoveItem event.
dialog.RemoveItem += myHandler;
最后,我们可以显示 ListDialogBox
并处理结果。我们还必须记住取消订阅 RemoveItem
事件:
dialog.ShowDialog();
dialog.RemoveItem -= myHandler;
//Process the result now.
if (!dialog.IsCancelled)
{
StringItem item = dialog.SelectedItem as StringItem;
MessageBox.Show("You added the topping \"" + item.DisplayName +
"\" to your pizza!");
}
剩下的就是将此代码放入您的应用程序并自行运行。上面的示例创建以下 ListDialogBox
:
此外,当点击意大利辣香肠上的“x”时,会显示提示:
关于c# - 如何创建通用列表框对话框控件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25184640/
如标题所示,ans_list是一个答案列表,ans_index是一个数字(答案在词汇表中的索引,但与atm无关) 这里生成的 tree.anslist 是什么? (例如,仅针对第一个),忽略迭代。 f
我目前将用户的输入存储在逗号分隔的列表中,如下所示: Userid | Options 1 | 1,2,5 用户在一个数组形式中勾选一组选项,然后用逗号连接起来 1,2,5 然后 MySQ
我目前将用户的输入存储在逗号分隔的列表中,如下所示: Userid | Options 1 | 1,2,5 用户在一个数组形式中勾选一组选项,然后用逗号连接起来 1,2,5 然后 MySQ
我想知道如何完全展平列表和包含它们的东西。除其他外,我想出了一个解决方案,它可以将具有多个元素的东西滑倒并将它们放回原处,或者在滑倒后将具有一个元素的东西拿走。 这与 How do I “flatte
我想知道如何完全展平列表和包含它们的东西。除其他外,我想出了一个解决方案,它可以将具有多个元素的东西滑倒并将它们放回原处,或者在滑倒后将带有一个元素的东西拿走。 这与 How do I “flatte
这个问题已经有答案了: Convert nested list to 2d array (3 个回答) 已关闭 7 年前。 java中有没有快捷方式可以转换 List> 到 String[][] ?
我在排序时遇到问题 List> 。我创建了一个自定义比较器,在其中编写了对数据进行排序的代码。 public class CustomComparator implements Comparator
这个问题已经有答案了: 已关闭10 年前。 Possible Duplicate: Java Generics: Cannot cast List to List? 我只是想知道为什么下面的java代
试图想出一个 LINQy 方法来做到这一点,但我什么也没想到。 我有一个对象列表<>,其中包含一个属性,该属性是逗号分隔的字母代码列表: lst[0].codes = "AA,BB,DD" lst[1
假设我有这些任务: points = [] point = (1, 2) 我怎么会这样做: points += point 它工作得很好,并且给了我点 = [1, 2]。但是,如果我这样做: poin
如何在 scala 中将 List[Task[List[Header]]] 类型转换为 Task[List[Header]]。 我有一个方法返回 Task[List[Header]] 并多次调用 do
如何在 Java 中查找二维列表的元素? 我有一个参数为 List> 的函数我想知道如何找到这个列表的行和列。 最佳答案 如果你喜欢 List> obj 然后你就可以像这样访问 obj.get(cur
分配 List到 List工作正常。 分配 List>到 List>不编译。 代码 public class Main { public static void main(String[] a
我正在用 Java 编写一个方法,该方法必须接收并迭代 Serializable 的 List。 有什么区别: public void myMethod(List list) { } 和 public
我看到很多人想用 mvvm 更新网格/列表/树的一部分,但他们不想刷新整个列表。 对于所有遇到此问题的人,我做了以下示例。 希望这对你有用。 最佳答案 这是一个简单的例子。整个代码中最重要的是: Bi
我正在为现有的 C++ 库编写包装器,该库使用列表,其中 T 是自定义结构。我被建议使用 vector 而不是列表,但我试图避免修改库。 为了更好地理解这个场景,我做了一个简单的应用程序,使用一个列表
List list List list 这两种声明有什么区别吗? 谢谢, 最佳答案 是的。 List可以包含所有派生自 Base 的不同事物的混合物. List包含同质项(从某种意义上说,它们必须全部
有人可以尽可能详细地解释以下类型之间的区别吗? List List List 让我更具体一点。我什么时候想使用 // 1 public void CanYouGiveMeAnAnswer(List l
我有一个元组列表,每个元组都是一对列表。所以我的数据看起来像: mylist = [(['foo', 'bar'], ['bar', 'bar']),(['bar', 'bar'],['bar', '
也许是一个时髦的标题,但我遇到了以下问题: 给定一个类型为 (a * b) list 的列表,我想创建一个类型为 (a * b list) list 的新列表。一个例子: 给定列表 let testL
我是一名优秀的程序员,十分优秀!