2011-08-11 67 views
2

我有一個自定義的TextBox控件。我試圖將它綁定到對象的簡單描述字符串屬性。我怎樣才能使綁定工作?如果將其更改爲TextBox,相同的綁定可以正常工作。WPF自定義文本框控件沒有正確綁定文本

<Style x:Key="{x:Type TaskDash:TextBoxWithDescription}" TargetType="{x:Type TaskDash:TextBoxWithDescription}"> 
      <Setter Property="Template"> 
       <Setter.Value> 
        <ControlTemplate TargetType="{x:Type TaskDash:TextBoxWithDescription}"> 

         <TextBox> 
         </TextBox> 

        </ControlTemplate> 
       </Setter.Value> 
      </Setter> 
     </Style> 
public class TextBoxWithDescription : TextBox 
{ 
    public TextBoxWithDescription() 
    { 
     LabelText = String.Empty; 
    } 

    public string LabelText { get; set; } 

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

     var textBlock = (TextBlock)this.Template.FindName("LabelText", this); 
     if (textBlock != null) textBlock.Text = this.LabelText; 
    } 
} 
<TaskDash:TextBoxWithDescription Grid.Row="0" Grid.Column="0" 
     x:Name="textBoxDescription" 
     Text="{Binding Description, BindsDirectlyToSource=True}" LabelText="Description"> 
    </TaskDash:TextBoxWithDescription> 
public partial class EditTaskItem : Window 
{ 
    private TaskItem _taskItem; 

    public EditTaskItem(TaskItem taskItem) 
    { 
     InitializeComponent(); 

     this.DataContext = taskItem; 
     textBoxDescription.DataContext = taskItem; 
     _taskItem = taskItem; 
    } 
} 

回答

6

好吧......有幾件事情你的代碼錯誤。 讓我們以您的自定義控件的風格開始。您需要添加一個靜態構造函數,它允許重新設置新控件。像這樣

static TextBoxWithDescription() 
    { 
     DefaultStyleKeyProperty.OverrideMetadata(typeof(TextBoxWithDescription), new FrameworkPropertyMetadata(typeof(TextBoxWithDescription))); 
    } 

這告訴WPF「嘿,請爲這個控件尋找樣式」。

現在您可以刪除x:Key="{x:Type TaskDash:TextBoxWithDescription}",因爲這將是您的默認樣式。

接下來的事情是。在WPF中有一點需要明白的是,除非獲得模板,否則每個控件都絕對沒有UI內容。您的樣式中已經有了一個模板,但它獲得的唯一可視內容是一個空的TextBox。這很奇怪,因爲你從TextBox派生TextBoxWithDescription。所以你創建的是一個從文本框派生的控件,包含一個文本框。 請參閱this以瞭解TextBox模板在WPF 4.0中的外觀。

返回ControlTemplate中的空文本框。記住我說過,沒有風格的你的控件是完全不可見的,它的唯一邏輯。唯一可見的是你的模板中的TextBox。爲了使它以某種方式工作,您需要將一些屬性傳遞給此TextBox。該控件說明了模板如何獲取這些屬性並將其投入使用。 您可以通過TemplateBinding 來做到這一點。如果你的控件有一個屬性背景,這個屬性什麼都不做,只要你願意就可以設置它,但是一個ControlTemplate可以給它一些含義。例如,在您的ControlTemplate中添加一個Rectangle,並將填充屬性設置爲{TemplateBinding Background}。哪些basicaly告訴矩形「您的財產填充將成爲我們當前模板化控件的背景值」。我希望這說得很清楚。

接下來的事情:你overrid OnApplyTemplate你通常這樣做是爲了找到你的控件模板的名字控制。看起來你把它與你的一個屬性混合在一起。要查找通過FindName你的模板控件,你需要給它一個名稱

喜歡這個

<Setter Property="Template"> 
       <Setter.Value> 
        <ControlTemplate TargetType="{x:Type TaskDash:TextBoxWithDescription}"> 

         <TextBox x:Name="PART_MyTextBox"> 
         </TextBox> 

        </ControlTemplate> 
       </Setter.Value> 
      </Setter> 

和修改OnApplyTemplate到

var textBlock = (TextBlock)this.Template.FindName("PART_MyTextBox", this); 

現在你有文本塊在當前的控件模板。

最後一個錯誤,我可以看到的是。 您在OnApplyTemplate設置TextBox文本到您的LabelText的。雖然這有效,但有一次,它不是很好,通常也不是WPF的方式。此外,如果您修改LabelText的財產,也不會被顯示出來,因爲你將不得不再次設定。 將您的LabelText更改爲dependency property現在您可以使用已經提到的TemplateBinding來直接在您的控件模板TextBox上設置此文本。

     <TextBox x:Name="PART_MyTextBox" Text="{TemplateBinding LabelText}> 
         </TextBox> 

這也將確保您的控件屬性的更改也將更新此文本框上的文本。

最後一件事

this.DataContext = taskItem; 
     // textBoxDescription.DataContext = taskItem; 
     _taskItem = taskItem; 

如果您textboxdescription將是你的窗口的父母,你不需要設置DataContext明確,因爲它會被繼承下來的層次結構。只要元素不會更改DataContext,它將始終是父級的DataContext。

我建議你閱讀更多關於WPF的基礎知識,我知道它有一個陡峭的學習曲線,但它的價值是值得的。如果有人來自WinForms背景,以便讓頭部環繞所有不同的新設計理念和不同方式來做事情,那很困難。

希望幫助一下

+0

感謝您的詳細反饋。我會在今天晚些時候嘗試更改模板。你對模板TextBox的看法是有道理的......我之前有過Rectangle或Border或者其中的東西,而不是TextBox。我認爲控制實際上是工作,但我遇到了一些其他問題,並決定更改爲TextBox。 – Shawn

0

@dowhilefor - 很好的答案。我只把它寫成答案,因爲評論太長了。

@Shawn - 看起來你正在試圖製作一個Label-Field控件。如果是這種情況,請嘗試以下模板:

<ControlTemplate TargetType="{x:Type TaskDash:TextBoxWithDescription}"> 
    <Grid> 
     <Grid.ColumnDefinitions> 
      <!--The SharedSizeGroup property will allow us to align all text boxes to the same vertical line--> 
      <ColumnDefinition Width="Auto" 
           SharedSizeGroup="LabelColumn"/> 
      <!--This column acts as a margin between the label and the text box--> 
      <ColumnDefinition Width="5"/> 
      <ColumnDefinition /> 
     </Grid.ColumnDefinitions> 
     <TextBlock Text="{TemplateBinding LabelText}" 
        VerticalAlignment="Center"/> 
     <Border Grid.Column="2" 
       Background="{TemplateBinding Background}" 
       BorderBrush="{TemplateBinding BorderBrush}" 
       BorderThickness="{TemplateBinding BorderThickness}"> 
      <ScrollViewer x:Name="PART_ContentHost" 
          Padding="{TemplateBinding Padding}"/> 
     </Border> 
    </Grid> 
</ControlTemplate> 

然後刪除OnApplyTemplate的覆蓋。

如果控件是(通常稱爲)「PropertyGrid」的一部分,並且在同一個面板中有多個TextBoxWithDescription控件實例,請在該面板上定義Grid.IsSharedSizeScope(它不一定是Grid面板)。這將使文本框與統一的垂直線對齊。