[翻譯] WPF 中用戶控制項 DataContext/Binding 和依賴屬性的問題

来源:https://www.cnblogs.com/weiliuhong/archive/2022/03/24/translate-user-control-datacontext_binding-issue-with-dependency-property-wpf.html
-Advertisement-
Play Games

[翻譯] WPF 中用戶控制項 DataContext/Binding 和依賴屬性的問題 目錄 提問 回答 User Control DataContext/Binding Issue with Dependency Property WPF [譯者] 獨立觀察員 2022 年 3 月 24 日 提問 ...


[翻译] WPF 中用户控件 DataContext/Binding 和依赖属性的问题

目录 提问 回答

User Control DataContext/Binding Issue with Dependency Property WPF

 

[译者] 独立观察员 2022 年 3 月 24 日

提问

ProgrammingDude(asked Dec 8, 2015 at 21:24)

Ok, so my problem is I have a user control. In the xaml I bind some colors to color properties that I have created as shown below.

好,我的问题是,我有一个用户控件,在 Xaml 中,我绑定了一些颜色到颜色属性,如下所示:

<GradientStop x:Name="stop1" Color="{Binding Color1}" Offset="0"/>
<GradientStop x:Name="stop2" Color="{Binding Color2}" Offset="1"/>

 

In my code behind I have a DependencyProperty that I have declared as shown below.

在后台代码中,我声明了一个依赖属性,如下所示:

public static readonly DependencyProperty IsActiveProperty =
    DependencyProperty.Register("IsActive", typeof(bool), typeof(Bin),
        new PropertyMetadata(new PropertyChangedCallback(Bin.IsActivePropertyChanged)));

 

The dependency property has a PropertyChangedCallback that it calls called IsActivePropertyChanged as shown below.

该依赖属性有一个 PropertyChangedCallback 方法,名称为 IsActivePropertyChanged,如下所示:

private static void IsActivePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    Bin b = (Bin)d;
    if((bool)e.NewValue)
    {
        b.Color1 = Color.FromArgb(0xFF, 0x3E, 0x3E, 0x3E);
        b.Color2 = Colors.Red;
        b.Color3 = Colors.Red;
        b.Color4 = Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF);
    }
    else
    {
        b.Color1 = Color.FromArgb(0xFF, 0x3E, 0x3E, 0x3E);
        b.Color2 = Color.FromArgb(0xFF, 0x83, 0x83, 0x83);
        b.Color3 = Color.FromArgb(0xFF, 0x63, 0x63, 0x63);
        b.Color4 = Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF);
    }
}

 

If I use the constructor below, the color changes inside of the constructor work fine, however, my IsActivePropertyChangedEvent never gets fired. I am assuming because of the DataContext assignment in the constructor.

如果我使用下面的构造函数,在构造函数中的颜色改变工作正常,然而,我的 IsActivePropertyChangedEvent 从未被触发。我估计是因为在构造函数中指定了 DataContext

public Bin()
{
    Color1 = Color.FromArgb(0xFF, 0x3E, 0x3E, 0x3E);
    Color2 = Color.FromArgb(0xFF, 0x83, 0x83, 0x83);
    Color3 = Color.FromArgb(0xFF, 0x63, 0x63, 0x63);
    Color4 = Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF);
    InitializeComponent();
    DataContext = this;
}

 

If I comment out the DataContext assignment and use the constructor below, my Color assignments do not work, but the IsActivePropertyChanged event fires fine.

如果我注释掉 DataContext 赋值,使用如下的构造函数,我的颜色赋值就没起作用,但 IsActivePropertyChanged 事件能够被触发。

public Bin()
{
    Color1 = Color.FromArgb(0xFF, 0x3E, 0x3E, 0x3E);
    Color2 = Color.FromArgb(0xFF, 0x83, 0x83, 0x83);
    Color3 = Color.FromArgb(0xFF, 0x63, 0x63, 0x63);
    Color4 = Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF);
    InitializeComponent();
    //DataContext = this;
}

 

My question is how can i get the binding to work correctly and have my event fire as well. I have tried setting the DataContext="{Binding RelativeSource={RelativeSource Self}}" (instead of setting the DataContext in the code behind) of the items that are bound to the Color Properties in XAML, a rectangle and a polygon, but that didn't seem to work. Thanks in advance for any help.

我的问题就是,怎样能让绑定和事件触发都正常工作。我尝试了为 Xaml 中绑定到 Color 属性的元素设置 DataContext="{Binding RelativeSource={RelativeSource Self}}" (而不是在后台代码中设置 DataContext),一个矩形和一个多边形,但似乎不起作用。提前感谢任何提供的帮助。

 

回答

vesan(answered Dec 9, 2015 at 22:48)

When writing your own control, you shouldn't mess with the DataContext of the control itself.

Instead, on the binding of the GradientStop, you can use RelativeSource={RelativeSource AncestorType=Bin} (assuming Bin is your control). Or you can define a template and use TemplateBinding. Check this answer I wrote a while back for a similar question - it has more detailed description of how this works.

在写你自己的控件时,你不应当把控件自身的 DataContext 弄乱。

取而代之的是,在绑定 GradientStop 时,你可以使用 RelativeSource={RelativeSource AncestorType=Bin} (假设 Bin 是你的控件)。或者,你可以定义一个模板并且使用 TemplateBinding。查看我不久之前为一个类似的问题写的 这个回答 ,其中有关于这如何工作的更多详细描述。

 

(译者注:他上面提到的详细回答如下)

vesan(answered May 28, 2015 at 2:26)

Although this has now been solved there seems to be some, in my opinion, inappropriate use of the DataContext.

When developing a custom reusable control, you should not set DataContext at all. What the DataContext will be, that is for the user of the control to decide, not for the developer. Consider the following common pattern of code:

虽然这个现在已经被解决了(译者注:另一个回答被标记为答案),在我看来,似乎有一些,对 DataContext  的不正确使用。

当开发一个定制的可复用的控件,你根本不应该设置 DataContext  。DataContext 将会是什么,应当是控件使用者来决定的,而不是开发者。思考下面的常见模式代码:

<Grid DataContext="{Binding Data}">
    <TextBox Text="{Binding TextValue1}" />
    <!-- Some more controls -->
</Grid>

 

Notice that here, you are using the Grid control. The developer of the control (in this case, the WPF team), didn't touch the DataContext at all - that is up to you. What does it mean for you as a control developer? Your DependencyProperty definition is fine, but you shouldn't touch the DataContext. How will you then bind something inside your control to the DependencyProperty value? A good way is using a template (namespaces omitted):

注意这里,你正在使用 Grid 控件。控件的开发者(这个例子中,是 WPF 团队),根本没有碰 DataContext —— 这个是你来用的。那么这对于作为控件开发者的你来说意味着什么呢?你的依赖属性定义是没问题的,但你不应该碰 DataContext 。那么你之后怎么将控件中的一些东西绑定到依赖属性的值呢?一个好的方式就是使用模板(命名空间省略了):

<MyTimePicker>
    <MyTimePicker.Template>
        <ControlTemplate TargetType="MyTimePicker">
            <!-- Stuff in your control -->
            <TextBlock Text="{TemplateBinding Time}" />
            <TextBox Text="{Binding Time, RelativeSource={RelativeSource TemplatedParent}}" />
        </ControlTemplate>
    <MyTimePicker.Template>
</MyTimePicker>

 

Note that TemplateBinding is always one-way only, so if you need any editing at all, you need to use normal binding (as you can see on the TextBox in the example).

This only means that the TextBlock/Box inside your control will get its Time value from your custom control itself, ignoring any DataContext you might have set.

注意 TemplateBinding 总是 one-way 的,所以如果你需要任何编辑,你需要使用普通的 binding (正如你在 TextBox 示例中看到的那样)。

这仅仅意味着你的控件内部的 TextBlock/Box 会从你自定义控件自身获得它的 Time 值,忽略任何你可能设置的 DataContext 。

 

Then, when you use the control, you do it like this (added to my first example):

然后,当你使用这个控件时,你会这样做(被添加到我的第一个示例):

<Grid DataContext="{Binding Data}">
    <TextBox Text="{Binding TextValue1}" />
    <!-- Some more controls -->
    <MyTimePicker Time="{Binding TimeValue}" />
</Grid>

 

What just happened here is that the MyTimePicker does not have DataContext set anywhere at all - it gets it from the parent control (the Grid). So the value goes like this: Data-->(binding)-->MyTimePicker.Time-->(template binding)-->TextBlock.Text.

这里发生的是, MyTimePicker 的 DataContext 根本没有在任何地方被设置,而是从父控件(Grid)获取。所以数值(译者注:这里是 TimeValue)像这样流转:Data-->(binding)-->MyTimePicker.Time-->(template binding)-->TextBlock.Text 

 

And above all, avoid doing this in the constructor of your custom control:

首要的是,避免在你的自定义控件的构造函数中这样做:

public MyTimePicker()
{
    InitializeComponent();
    DataContext = this;
}

 

This will override any DataContext set in XAML, which will make binding a huge pain (because you'll have to always set Source manually). The previous example would not work, and this wouldn't work either:

这会覆盖在 Xaml 中设置的任何 DataContext ,会使得绑定变成一个大痛苦(因为你将不得不总是手动设置 Source )。之前的示例将不能工作,下面这个也不能工作:

<MyTimePicker DataContext="{Binding Data}" Time="{Binding TimeValue}" />

 

You would think this is OK, but the DataContext will be resolved in the InitializeComponent() call, so the value will be immediately overwritten. So the binding to TimeValue will look for it in the control instead (which will, of course, fail).

你可能认为这是可以的,但 DataContext 会在 InitializeComponent() 调用中被重新处理,所以值会被立即重写。所以目标为 TimeValue 的绑定反而会在控件中搜寻(这个当然会失败)。

 

Just don't touch the DataContext when developing a control and you'll be fine.

(总之,)开发一个控件时就别碰 DataContext ,你将会一切顺利。

 

原创文章,转载请注明: 转载自 独立观察员

本文链接地址: [翻译] WPF 中用户控件 DataContext/Binding 和依赖属性的问题 [http://dlgcy.com/translate-user-control-datacontext_binding-issue-with-dependency-property-wpf/]

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Java 的模塊在Java 9中正式實裝,一直沒時間來研究一下這個東西,今天就和大家一起學習一下這個功能。 Java模塊解決了什麼問題 最近很多同學問我,胖哥,該怎麼學習?該學習什麼?這裡胖哥也穿插說一下。不管學東西,一定要先搞清楚學了有什麼用,是學了馬上就能用上還是以後有用。我覺得在時間有限的情況 ...
  • 又到了分享Python小技能的時間了,今天教大家如何爬自己的微信好友。等會就可以拿自己微信好友練練手,這波操作聽起來就不錯的樣子,準備好了嗎?開始了..... 今天這篇文章會基於Python對微信好友進行數據分析,這裡選擇的維度主要有:性別、頭像、簽名、位置,主要採用圖表和詞雲兩種形式來呈現結果,其 ...
  • 阿珍探出頭看了看老徐的屏幕,全部都是綠色的曲線圖,好奇地問:“老徐,你看的這是什麼?”老徐看的太入神,轉過頭才發現阿珍,尬尷地笑了笑說:“我就是看看最近的行情。”老徐立馬切換了視窗。 ...
  • oop面向對象的程式開發 用幾大特征表達這一類事務稱為一個類,類更像是一張圖紙,表達的是一個抽象概念 對象是類的具體實現,更像是由這種圖紙產出的具體物品,類只有一個,但對象可以通過這個類實例化出多個 對象是類的實例,類是對象的模板 *類中的成員只有方法和屬性,不要裸露的把判斷和迴圈直接寫在類中,而是 ...
  • 集合嵌套和遍歷元素 package Day16; import java.util.ArrayList; public class LX15 { public static void main(String[] args) { //創建集合1 規定其類型為學生類型 ArrayList<Student ...
  • 作者:三分惡 原文:cnblogs.com/three-fighter/p/14054749.html 博主負責的項目報了一個問題,用戶操作回退失效。我們的設計里,操作回退是回到操作前的狀態。經過查看日誌發現,用戶之前的操作做了兩次,也就是說提交操作的介面被調用了兩次,導致之用戶上一次的狀態和這一次 ...
  • 可能平常會遇到一些需求,比如構建菜單,構建樹形結構,資料庫一般就使用父id來表示,為了降低資料庫的查詢壓力,我們可以使用Java8中的Stream流一次性把數據查出來,然後通過流式處理。 我們一起來看看,代碼實現為了實現簡單,就模擬查看資料庫所有數據到List裡面。 實體類:Menu.java /* ...
  • 常用反編譯工具 1.JetBrains Peek 2..Net Reflector 3.ILspy 4.dnSpy下載 這篇筆記主要記錄如何使用dnSpy進行反編譯調試,dnSpy除了web程式,也可以調試Windows服務,Winform桌面程式,使用方法都是一致的,主要用來解決線上在測試環境中無 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...