开发者

WPF ListBox Custom Control issues

In WPF, I am creating a simple custom control for my TODO program. It should do the following:

  • Show up as a ListBox with an Add and Remove button above it.
  • The Add and Remove buttons should add and remove items from my base class.
    • I have this working
  • On pressing F2, I want the list box items to change into a TextBox control.

My main issues/Questions are:

  • On OnKeyDown, I get the error: This operation is valid only on elements that have this template applied. How do I get around this? This is where I want to press F2, and to be able to make the TextBox visible and Label invisible.
  • Is there a better way to do this? If my control is a container for buttons AND a text box, should I not be inheriting from ListBox? Should I inherit from Control instead and make it a container?

    • If so, how do I implement it? Do I basically use the following code in the style, and remove the overrides like OnKeyDown and instead register the KeyDown event for the Textbox? I'll give that a try after this post but let me know if you have any advice.

I had something close working before, in the following code, but now I want this moved from my main Window XAML to the custom control code, and I want to be able to edit on button press:

<!-- In my MainWindow.XAML -->
<TaskDashControls:ListBoxWithAddRemove x:Name="listBoxItems" Grid.Row="1" Grid.Column="3" Grid.RowSpan="3"
        ItemsSource="{Binding}">
    <TaskDashControls:ListBoxWithAddRemove.ItemTemplate>
        <DataTemplate>
            <DockPanel>
                <Button DockPanel.Dock="Left" Click="SelectItemClick">SELECT</Button>
                <TextBlock x:Name="LabelDescription" Visibility="Visible"  DockPanel.Dock="Left" Text="{Binding Description}" Height="25" Width="150" />
                <TextBox x:Name="EditableDescription" Visibility="Collapsed" DockPanel.Dock="Left" Text="{Binding Description}" Height="25" Width="150" />
                <Button DockPanel.Dock="Left" Click="EditTaskItemClick">EDIT</Button>
            </DockPanel>
        </DataTemplate>
    </TaskDashControls:ListBoxWithAddRemove.ItemTemplate>
</TaskDashControls:ListBoxWithAddRemove>

I have now removed the DataTemplate, to move it over to the custom control:

<!-- In my MainWindow.XAML -->
<TaskDashControls:ListBoxWithAddRemove x:Name="listBoxItems" Grid.Row="1" Grid.Column="3" Grid.RowSpan="3"
        ItemsSource="{Binding}"/>

Here is the custom control in Generic.XAML:

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TaskDash.Controls">


<SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />
<SolidColorBrush开发者_开发问答 x:Key="SolidBorderBrush" Color="#888" />
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />
<SolidColorBrush x:Key="DisabledBorderBrush" Color="#AAA" />


<Style TargetType="{x:Type TextBox}">
    <Setter Property="Margin" Value="2" />
</Style>


<Style x:Key="{x:Type local:ListBoxWithAddRemove}" TargetType="{x:Type local:ListBoxWithAddRemove}">
    <Setter Property="Margin" Value="3" />
    <Setter Property="SnapsToDevicePixels" Value="True"/>
    <Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
    <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
    <Setter Property="MinWidth" Value="120"/>
    <Setter Property="MinHeight" Value="20"/>
    <Setter Property="AllowDrop" Value="true"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="25" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>

                    <!--<Button Grid.Column="0" Grid.Row="0" x:Name="DeleteButton"
                                    Click="DeleteControlClick">Delete</Button>
                    <Button Grid.Column="1" Grid.Row="0" x:Name="AddButton"
                                    Click="AddControlClick">Add</Button>-->
                    <Button Grid.Column="0" Grid.Row="0" x:Name="DeleteButton">Delete</Button>
                    <Button Grid.Column="1" Grid.Row="0" x:Name="AddButton">Add</Button>
                    <Border 
                                Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2"
                                  Name="Border" 
                                  Background="{StaticResource WindowBackgroundBrush}"
                                  BorderBrush="{StaticResource SolidBorderBrush}"
                                  BorderThickness="1"
                                  CornerRadius="2">
                        <ScrollViewer 
                                    Margin="0"
                                    Focusable="false">
                            <StackPanel Margin="0" IsItemsHost="True" />
                        </ScrollViewer>

                        <!--<ListBox ItemTemplate="{TemplateBinding ItemTemplate}">
                            <DataTemplate>
                                <DockPanel>
                                    <TextBlock x:Name="LabelDescription" Visibility="Visible"  DockPanel.Dock="Left" Text="{Binding Description}" Height="25" Width="150" />
                                    <TextBox x:Name="EditableDescription"  DockPanel.Dock="Left" Text="{Binding Description}" Height="25" Width="150" />
                                </DockPanel>
                            </DataTemplate>
                        </ListBox>-->

                    </Border>
                </Grid>
            </ControlTemplate>

        </Setter.Value>
    </Setter>
</Style>

Here is my custom control class

using System;
using System.Windows;
using System.Windows.Controls;

namespace TaskDash.Controls
{
[TemplatePart(Name = "Text", Type = typeof(TextBox))]
[TemplatePart(Name = "LabelText", Type = typeof(TextBlock))]
public class TextBoxWithDescription : Control
{
    static TextBoxWithDescription()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(TextBoxWithDescription), new FrameworkPropertyMetadata(typeof(TextBoxWithDescription)));
    }

    public TextBoxWithDescription()
    {
        LabelText = String.Empty;
        Text = String.Empty;
    }

    public static readonly DependencyProperty LabelTextProperty =
        DependencyProperty.Register("LabelText", typeof(string), typeof(TextBoxWithDescription),
        new PropertyMetadata(string.Empty, OnLabelTextPropertyChanged));
    private static void OnLabelTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {

    }
    public string LabelText
    {
        get { return GetValue(LabelTextProperty).ToString(); ; }
        set { SetValue(LabelTextProperty, value); }
    }

    // http://xamlcoder.com/cs/blogs/joe/archive/2007/12/13/building-custom-template-able-wpf-controls.aspx
    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(string), typeof(TextBoxWithDescription),
        new UIPropertyMetadata(null,
                                new PropertyChangedCallback(OnTextChanged)
                           ));
    private static void OnTextChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        TextBoxWithDescription textBox = o as TextBoxWithDescription;
        if (textBox != null)
            textBox.OnTextChanged((String)e.OldValue, (String)e.NewValue);
    }
    protected virtual void OnTextChanged(String oldValue, String newValue)
    {
        // fire text changed event
        this.Text = newValue;
        this.RaiseEvent(new RoutedEventArgs(TextChangedEvent, this));
    }
    public string Text
    {
        get { return GetValue(TextProperty).ToString(); }
        set { SetValue(TextProperty, value); }
    }
    public static readonly RoutedEvent TextChangedEvent =
        EventManager.RegisterRoutedEvent("TextChanged",
                                RoutingStrategy.Bubble,
                                typeof(RoutedEventHandler),
                                typeof(TextBoxWithDescription));
    public event RoutedEventHandler TextChanged
    {
        add { AddHandler(TextChangedEvent, value); }
        remove { RemoveHandler(TextChangedEvent, value); }
    }



    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        //var textBlock = (TextBlock)this.Template.FindName("LabelText", this);
        //if (textBlock != null) textBlock.Text = this.LabelText;


        //var textBox = (TextBox)this.Template.FindName("Text", this);
        //if (textBox != null) textBox.Text = this.Text;
    }
}
}


I would create a class for TODO with Properties: String Desc, Visibility txtBox, Visibility txtBlock. Then items source for the TaskDashControls is List TODO. In xaml you can bind the visibilty property. In the TODO class you can control that when one is visible the other is not.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜