Skip to content

Commit f1c8767

Browse files
Make InPlaceTextEditor example more interesting using IEditableObject and MVVM x:Bind
1 parent 9eb63f5 commit f1c8767

File tree

4 files changed

+73
-9
lines changed

4 files changed

+73
-9
lines changed

components/Adorners/samples/Adorners.Samples.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
<LangVersion>preview</LangVersion>
77
</PropertyGroup>
88

9+
<ItemGroup>
10+
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
11+
</ItemGroup>
12+
913
<!-- Sets this up as a toolkit component's sample project -->
1014
<Import Project="$(ToolingDirectory)\ToolkitComponent.SampleProject.props" />
1115
</Project>

components/Adorners/samples/Adorners.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,13 @@ The above examples highlights how adorners are sized and positioned directly ato
5252

5353
## Custom Adorner Example
5454

55-
Adorners can be subclassed in order to encapsulate specific logic and/or styling for your scenario. For instance, you may want to create a custom Adorner that allows a user to edit a piece of text in place:
55+
Adorners can be subclassed in order to encapsulate specific logic and/or styling for your scenario.
56+
For instance, you may want to create a custom Adorner that allows a user to click and edit a piece of text in place.
57+
The following example uses `IEditableObject` to control the editing lifecycle coordinated with a typical MVVM pattern binding:
5658

5759
> [!SAMPLE InPlaceTextEditorAdornerSample]
5860
59-
Adorners are templated controls, but you can use a class-backed resource dictionary to better enable usage of x:Bind for easier creation.
61+
Adorners are template-based controls, but you can use a class-backed resource dictionary to better enable usage of x:Bind for easier creation and binding to the `AdornedElement`, as seen here.
6062

6163
## TODO: Resize Example
6264

components/Adorners/samples/InPlaceTextEditor/InPlaceTextEditorAdornerSample.xaml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,12 @@
6868
</Page.Resources>
6969

7070
<StackPanel Spacing="8">
71-
<TextBlock Text="Editable Text:" />
71+
<TextBlock Text="Click the editable text below to edit:" />
7272

73-
<TextBlock Text="Hello, World!">
73+
<!-- We set the DataContext here for our Adorner to retrieve the IEditableObject reference -->
74+
<!-- We use TwoWay binding to ensure the updates from the Adorner are reflected in the ViewModel -->
75+
<TextBlock DataContext="{x:Bind ViewModel}"
76+
Text="{x:Bind ViewModel.MyText, Mode=TwoWay}">
7477
<ui:AdornerLayer.Xaml>
7578
<!-- Style manually set here as local to example -->
7679
<local:InPlaceTextEditorAdorner Style="{StaticResource DefaultInPlaceTextEditorAdornerStyle}"/>

components/Adorners/samples/InPlaceTextEditor/InPlaceTextEditorAdornerSample.xaml.cs

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,66 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
using CommunityToolkit.Mvvm.ComponentModel;
56
using CommunityToolkit.WinUI;
67

78
namespace AdornersExperiment.Samples.InPlaceTextEditor;
89

910
[ToolkitSample(id: nameof(InPlaceTextEditorAdornerSample), "In place text editor Adorner", description: "A sample for showing how add a popup TextBox component via an Adorner of a TextBlock.")]
1011
public sealed partial class InPlaceTextEditorAdornerSample : Page
1112
{
13+
public MyViewModel ViewModel { get; } = new();
14+
1215
public InPlaceTextEditorAdornerSample()
1316
{
1417
this.InitializeComponent();
1518
}
1619
}
1720

21+
/// <summary>
22+
/// ViewModel that shows using <see cref="IEditableObject"/> in conjunction with an Adorner.
23+
/// </summary>
24+
public partial class MyViewModel : ObservableObject, IEditableObject
25+
{
26+
[ObservableProperty]
27+
public partial string MyText { get; set; } = "Hello, World!";
28+
29+
bool _isEditing = false;
30+
private string _backupText = string.Empty;
31+
32+
public void BeginEdit()
33+
{
34+
if (!_isEditing)
35+
{
36+
_backupText = MyText;
37+
_isEditing = true;
38+
}
39+
}
40+
41+
public void CancelEdit()
42+
{
43+
if (_isEditing)
44+
{
45+
MyText = _backupText;
46+
_isEditing = false;
47+
}
48+
}
49+
50+
public void EndEdit()
51+
{
52+
if (_isEditing)
53+
{
54+
_backupText = MyText;
55+
_isEditing = false;
56+
}
57+
}
58+
}
59+
60+
/// <summary>
61+
/// An Adorner that shows a popup TextBox for editing a TextBlock's text.
62+
/// If that TextBlock's DataContext implements <see cref="IEditableObject"/>,
63+
/// it will be used to manage the editing session.
64+
/// </summary>
1865
public sealed partial class InPlaceTextEditorAdorner : Adorner<TextBlock>
1966
{
2067
/// <summary>
@@ -30,9 +77,7 @@ public bool IsPopupOpen
3077
/// Identifies the <see cref="IsPopupOpen"/> dependency property.
3178
/// </summary>
3279
public static readonly DependencyProperty IsPopupOpenProperty =
33-
DependencyProperty.Register("IsPopupOpen", typeof(bool), typeof(InPlaceTextEditorAdorner), new PropertyMetadata(false));
34-
35-
private string _originalText = string.Empty;
80+
DependencyProperty.Register(nameof(IsPopupOpen), typeof(bool), typeof(InPlaceTextEditorAdorner), new PropertyMetadata(false));
3681

3782
public InPlaceTextEditorAdorner()
3883
{
@@ -63,18 +108,28 @@ protected override void OnDetaching()
63108

64109
private void AdornedElement_Tapped(object sender, TappedRoutedEventArgs e)
65110
{
66-
_originalText = AdornedElement?.Text ?? string.Empty;
111+
if (AdornedElement?.DataContext is IEditableObject editableObject)
112+
{
113+
editableObject.BeginEdit();
114+
}
67115
IsPopupOpen = true;
68116
}
69117

70118
public void ConfirmButton_Click(object sender, RoutedEventArgs e)
71119
{
120+
if (AdornedElement?.DataContext is IEditableObject editableObject)
121+
{
122+
editableObject.EndEdit();
123+
}
72124
IsPopupOpen = false;
73125
}
74126

75127
public void CloseButton_Click(object sender, RoutedEventArgs e)
76128
{
77-
AdornedElement?.Text = _originalText;
129+
if (AdornedElement?.DataContext is IEditableObject editableObject)
130+
{
131+
editableObject.CancelEdit();
132+
}
78133
IsPopupOpen = false;
79134
}
80135
}

0 commit comments

Comments
 (0)