I'm trying to make my fist UserControl in C#. It is a TabControll with some quality of life improvements. The goal is to be able to use it in various projects, so it has to be as generic as possible.
So far I have exposed the ItemSource through a DependencyProperty. But I'm suck with how to do the same with the ContentTemplate Property.
Here's an example of my code so far:
XAML:
<UserControl ...>
<UserControl.Resources>
<!-- some styles and templates -->
</UserControl.Resources>
<TabControl ItemsSource="{Binding ItemsSource}" SelectedIndex="{Binding selectedIndex}"
Style="{StaticResource FixatedTabControl}" ItemTemplateSelector="{StaticResource myDataTemplateSelector}"/>
</UserControl>
The code behind:
public partial class DynamicTabControl : UserControl, INotifyPropertyChanged
{
public DynamicTabControl()
{
InitializeComponent();
this.DataContext = this;
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable<ITabItem>), typeof(DynamicTabControl));
public IEnumerable<ITabItem> ItemsSource
{
get { return (IEnumerable<ITabItem>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
}
I can use the DynamicTabControl like so:
<Window x:Class="Demo.MainWindow"
...
xmlns:local="clr-namespace:Demo"
xmlns:foo="clr-namespace:DynamicTabUserControl"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<foo:DynamicTabControl x:Name="testTabContr" ItemsSource="{Binding data}">
</foo:DynamicTabControl>
</Grid>
</Window>
But how can I make it possible to alter/add the contenTemplate of the UserControl's TabControl?
I would like to get it to behave like such:
<Window x:Class="Demo.MainWindow"
...
xmlns:local="clr-namespace:Demo"
xmlns:foo="clr-namespace:DynamicTabUserControl"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<foo:DynamicTabControl x:Name="testTabContr" ItemsSource="{Binding data}">
<foo:DynamicTabControl.TabControl.ContentTemplate>
<DataTemplate>
<TextBox Text="{Binding someData}"/>
</DataTemplate>
</foo:DynamicTabControl.TabControl.ContentTemplate>
</foo:DynamicTabControl>
</Grid>
</Window>
I'm still learning, so please help me out.
Thank you in advance.
Add another dependency property to the UserControl:
public partial class DynamicTabControl : UserControl, INotifyPropertyChanged
{
public DynamicTabControl()
{
InitializeComponent();
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable<ITabItem>), typeof(DynamicTabControl));
public IEnumerable<ITabItem> ItemsSource
{
get { return (IEnumerable<ITabItem>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty TabContentTemplateProperty =
DependencyProperty.Register("TabContentTemplate", typeof(DataTemplate), typeof(DynamicTabControl));
public DataTemplate TabContentTemplate
{
get { return (DataTemplate)GetValue(TabContentTemplateProperty); }
set { SetValue(TabContentTemplateProperty, value); }
}
}
...and bind to it:
<UserControl>
<UserControl.Resources>
<!-- some styles and templates -->
</UserControl.Resources>
<TabControl ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource AncestorType=UserControl}}"
Style="{StaticResource FixatedTabControl}"
ContentTemplate="{Binding TabContentTemplate, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</UserControl>
You can then set this property in the XAML markup of the window:
<foo:DynamicTabControl x:Name="testTabContr" ItemsSource="{Binding data}">
<foo:DynamicTabControl.TabContentTemplate>
<DataTemplate>
<TextBox Text="{Binding someData}"/>
</DataTemplate>
</foo:DynamicTabControl.TabContentTemplate>
</foo:DynamicTabControl>
Related
I have a user control like this
<UserControl
x:Class="App41.UserCntrl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
<Grid>
<ListView SelectionMode="Multiple" x:Name="lview" ItemsSource="{x:Bind ItemsSource, Mode=OneWay}" >
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{x:Bind PropertyNameToBind, Mode=OneWay}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
code behind
public sealed partial class UserCntrl : UserControl
{
public UserCntrl()
{
this.InitializeComponent();
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(UserControl),
new PropertyMetadata(null));
public IEnumerable ItemsSource
{
get => (IEnumerable)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
public string PropertyNameToBind
{
get { return (string)GetValue(PropertyNameToBindProperty); }
set { SetValue(PropertyNameToBindProperty, value); }
}
public static readonly DependencyProperty PropertyNameToBindProperty =
DependencyProperty.Register("PropertyNameToBind", typeof(string), typeof(UserControl), new PropertyMetadata(""));
}
And I am calling this user control in my main page like this
<Page
x:Class="App41.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App41" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
<Grid>
<local:UserCntrl x:Name="lview" Loaded="EditTextControl_Loaded"></local:UserCntrl>
</Grid>
Code behind
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private void EditTextControl_Loaded(object sender, RoutedEventArgs e)
{
ObservableCollection<OptionItem> io = new ObservableCollection<OptionItem>();
io.Add(new OptionItem { Name = "11111111111" });
lview.ItemsSource = io;
lview.PropertyNameToBind = "Name";
}
}
public class OptionItem
{
private string _Name = string.Empty;
public string Name
{
get { return _Name; }
set { _Name = value; }
}
}
All looks good to me, but ListView displays empty items instead of my content. I believe the issue is in this line
<TextBlock Text="{x:Bind PropertyNameToBind, Mode=OneWay}" />
Where I am trying to bing Name property inside the OptionItem Model. How can I solve this?
In UWP, each DataTemplate corresponds to an item in the ItemsSource, so the DataContext of the DataTemplate is limited to the item itself, specific to your project, is OptionItem.
You use {x:Bind PropertyNameToBind} is actually trying to bind OptionItem.PropertyNameToBind, but your OptionItem does not have this property, so nothing will be displayed.
Please determine whether you want to bind PropertyNameToBind or OptionItem.Name property, if it is OptionItem.Name, please use {x:Bind Name}.
If not, try this:
<UserControl
...
x:Name="Main">
<Grid>
<ListView SelectionMode="Multiple" x:Name="lview" ItemsSource="{x:Bind ItemsSource, Mode=OneWay}" >
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ElementName=Main,Path=PropertyNameToBind}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</UserControl>
Update
I think I understand what you mean, but you have some misunderstandings about binding.
What you bind is a property, and you pass a string value. This value cannot be used as a property name to reflect the properties of the binding class.
Combined with your needs, I recommend that you use the interface for dynamic processing:
interface
public interface ITest
{
string GetDisplayText();
}
class
public class OptionItem : ITest
{
public string Name { get; set; } = string.Empty;
public string GetDisplayText()
{
return Name;
}
}
control
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:ITest">
<TextBlock Text="{x:Bind GetDisplayText()}" />
</DataTemplate>
</ListView.ItemTemplate>
By creating an interface, a method for outputting displayed text is provided.
Any class that inherits this interface can override this method according to their needs.
Inside the control, you only need to call the GetDisplayText() method, regardless of which class is bound.
<Grid>
<ListView SelectionMode="Multiple" x:Name="lview" ItemsSource="{x:Bind ItemSource, Mode=OneWay}" >
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{x:Bind OptionItem.Name, Mode=OneWay}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Grid>
I have a WPF user control which uses a property from my viewmodel for binding. I now want to use two instances of that user control with the same viewmodel, but override the binding in one of them.
The code looks like this
User control
<UserControl x:Class="TestWpfApp.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestWpfApp">
<Grid>
<DockPanel>
<Label Margin="10" Content="{Binding MyLabel}"/>
<Button Margin="10" Content="{Binding MyButton}"/>
</DockPanel>
</Grid>
</UserControl>
Main view
<Window x:Class="TestWpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestWpfApp"
Title="MainWindow" Height="150" Width="525">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<Grid>
<DockPanel>
<local:UserControl1 DockPanel.Dock="Top" x:Name="UserControl1"/>
<!--In this instance of UserControl1, I want the Label to bind to the MyNewLabel-->
<local:UserControl1 DockPanel.Dock="Top" x:Name="UserControl2"/>
</DockPanel>
</Grid>
</Window>
Viewmodel
public class ViewModel
{
public string MyLabel => "Label1";
public string MyButton => "Button1";
public string MyNewLabel => "Label2";
}
In the second instance of UserControl1, I would like the label to be bound to a different property. I tried setting that property as a resource on the user control, and then overriding it in the main view but I wasn't able to get that to work.
In reality, I'm using DevExpress controls and POCO viewmodels, if that makes things easier.
When creating custom control - it's not a good idea to bind controls inside that custom control to arbitrary properties provided by external data context. Not only you are getting problems like you have now, user of that control has no idea which datacontext with which properties should he provide for that control to work. Only looking at source code of your UserControl one might find that it expects data context with properties MyLabel and MyButton. Instead - make control self-contained by introducing dependency properties on control itself:
public partial class UserControl1 : UserControl {
public UserControl1() {
InitializeComponent();
}
public string Text
{
get { return (string) GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(UserControl1), new PropertyMetadata(null));
public string ButtonText
{
get { return (string) GetValue(ButtonTextProperty); }
set { SetValue(ButtonTextProperty, value); }
}
public static readonly DependencyProperty ButtonTextProperty =
DependencyProperty.Register("ButtonText", typeof(string), typeof(UserControl1), new PropertyMetadata(null));
}
And bind to those properties:
<UserControl x:Class="WpfApplication1.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="self">
<Grid>
<DockPanel>
<Label Margin="10"
Content="{Binding Text, ElementName=self}" />
<Button Margin="10"
Content="{Binding ButtonText, ElementName=self}" />
</DockPanel>
</Grid>
</UserControl>
Then in main window - bind those dependency properties to your model:
<Window.DataContext>
<my:ViewModel />
</Window.DataContext>
<Grid>
<DockPanel>
<my:UserControl1 DockPanel.Dock="Top"
x:Name="UserControl1" Text="{Binding MyLabel}" ButtonText="{Binding MyButton}" />
<!--In this instance of UserControl1, I want the Label to bind to the MyNewLabel-->
<my:UserControl1 DockPanel.Dock="Top"
x:Name="UserControl2"
Text="{Binding MyNewLabel}"
ButtonText="{Binding MyButton}" />
</DockPanel>
</Grid>
I'm following this How to create tab-able content in WPF/C#? but I want each tab to show a datagrid. The datagrid doesn't show and also doesn't show the data. When I step into the code, I do see 0,1 being set.
MainWindow.xaml
<Window x:Class="MVVMDataInstances.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:viewModel="clr-namespace:MVVMDataInstances"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<viewModel:MainWindowViewModel />
</Window.DataContext>
<Grid>
<Grid.Resources>
<DataTemplate x:Key="ContentTemplate"
DataType="{x:Type viewModel:ChildViewModel}"/>
</Grid.Resources>
<TabControl ContentTemplate="{StaticResource ContentTemplate}"
ItemsSource="{Binding Items}" />
</Grid>
</Window>
MainWindowViewModel.cs
public ObservableCollection<ChildViewModel> Items { get; private set; }
public MainWindowViewModel()
{
Items = new ObservableCollection<ChildViewModel> {new ChildViewModel(0), new ChildViewModel(1)};
}
ChildView.xaml
<UserControl x:Class="MVVMDataInstances.View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ViewModel="clr-namespace:MVVMDataInstances"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<ViewModel:ChildViewModel/>
</UserControl.DataContext>
<DataGrid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding Grid, Mode=TwoWay}" />
</UserControl>
ChildViewModel.cs
public class ChildViewModel : ViewModelBase
{
private ObservableCollection<ChildModel> _grid;
public ObservableCollection<ChildModel> Grid
{
get { return _grid; }
private set
{
_grid = value;
OnPropertyChanged("Grid");
}
}
public ChildModel Data { get; set; }
public ChildViewModel()
{
}
public ChildViewModel(int tabNumber)
{
Data = new ChildModel {A = tabNumber.ToString(CultureInfo.InvariantCulture)};
Grid = new ObservableCollection<ChildModel> {Data};
}
}
ChildModel.cs
public class ChildModel
{
public string A { get; set; }
public string B { get; set; }
}
ViewModelBase.cs
public class ViewModelBase : INotifyPropertyChanged
{
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
I would like to see one grid per tab. The entry on the first tab has a value of 0 for property A. The entry of the second tab has a value of 1 for the property B.
I see that when OnPropertyChanged is called PropertyChanged is null.
I can access the datagrid if I have this in MainWindow.xaml
<Grid>
<TabControl ItemsSource="{Binding Items}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding TabTitle}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<DataGrid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" AutoGenerateColumns="True"
ItemsSource="{Binding Grid}"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
But OnPropertyChanged is always null for this and I don't see the grid
<DataTemplate x:Key="ContentTemplate"
DataType="{x:Type viewModel:ChildViewModel}">
<viewModel:ChildView />
</DataTemplate>
Your DataTemplate is empty. From your question, you probably want to do something along these lines:
<Grid>
<Grid.Resources>
<DataTemplate x:Key="ContentTemplate" TargetType="{x:Type viewModel:ChildViewModel}">
<DataGrid AutoGenerateColumns="False" . . .>
<DataGrid.Columns>
<!-- Your column definitions here -->
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</Grid.Resources>
<TabControl ContentTemplate="{StaticResource ContentTemplate}"
ItemsSource="{Binding Items}" />
</Grid>
This DataTemplate tells the ChildViewModel.cs that it's appearance is View.xaml :
<DataTemplate x:Key="ContentTemplate"
DataType="{x:Type viewModel:ChildViewModel}">
<viewModel:View />
</DataTemplate>
As a result behind the scenes it also sets each View's DataContext to an instance of ChildViewModel.
I follow this MVVM: ViewModel inheritance to have ViewModel Inheritance. The reason PropertyChanged was null because the constructor was initialized twice. First time with an integer, and then second time with the InitializedComponent. If I comment out the InitializedComponent, the grid was never initialized. The answer to the other stackoverflow question makes everything cleaner with view model inheritance.
When I run this simple wpf app, I get a blank window. Any ideas what I'm doing wrong?
//MainWindow.xaml.cs
public string SimpleText {get;set;}
public MainWindow()
{
InitializeComponent();
SimpleText = "this is a test";
}
//MainWindow.xaml
<StackPanel>
<TextBlock Text="{Binding SimpleText}" Width="200"/>
</StackPanel>
DataContext is a way to go but you can also use RelativeSource markup extension to get window's property:
<TextBlock Text="{Binding SimpleText, RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=Window}}" Width="200"/>
You must set the DataContext:
public MainWindow()
{
InitializeComponent();
SimpleText = "this is a test";
this.DataContext = this;
}
As an alternative, you can set DataContext on the side XAML like this:
XAML
<Window x:Class="TextBlockDontBind.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:this="clr-namespace:TextBlockDontBind"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<this:TestData />
</Window.DataContext>
<StackPanel>
<TextBlock Text="{Binding SimpleText}" Width="200"/>
</StackPanel>
</Window>
Code-behind
public class TestData
{
private string _simpleText = "this is a test";
public string SimpleText
{
get
{
return _simpleText;
}
set
{
_simpleText = value;
}
}
}
But in this case to update a property, for a Class must implement the INotifyPropertyChanged interface.
I've some simple code below that uses a ToggleButton.IsChecked property to set the Visibility of a TextBlock. It works fine. Since this doesn't quite fit in with my program's structure, I'm trying to bind the visibility of another TextBlock to a DependencyProperty of "this". It compiles fine, but it produces no effect. I'm doing something wrong, just not sure what.
XAML
<Window x:Class="ToggleButtonTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="200" Height="100">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<StackPanel>
<ToggleButton x:Name="toggleButton" Content="Toggle"
IsChecked="True" Checked="toggleButton_Checked"/>
<TextBlock Text="Some Text"
Visibility="{Binding IsChecked,
ElementName=toggleButton,
Converter={StaticResource BooleanToVisibilityConverter}}"/>
<TextBlock Text="More Text"
Visibility="{Binding ShowMoreText,
ElementName=this,
Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
</Window>
C#
using System.Windows;
namespace ToggleButtonTest
{
public partial class MainWindow : Window
{
static MainWindow()
{
FrameworkPropertyMetadata meta =
new FrameworkPropertyMetadata(true,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault);
ShowMoreTextProperty =
DependencyProperty.Register("ShowMoreText",
typeof(bool), typeof(MainWindow), meta);
}
public MainWindow()
{
InitializeComponent();
}
public static readonly DependencyProperty ShowMoreTextProperty;
public bool ShowMoreText
{
get
{
return (bool)GetValue(ShowMoreTextProperty);
}
set
{
SetValue(ShowMoreTextProperty, value);
}
}
private void toggleButton_Checked(object sender, RoutedEventArgs e)
{
ShowMoreText = toggleButton.IsChecked.Value;
}
}
}
Edit:
Having had this answered, I want to post my working code...
XAML
<Window x:Class="ToggleButtonTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="200" Height="100"
Name="thisWindow">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<StackPanel>
<ToggleButton x:Name="toggleButton"
Content="Toggle"
IsChecked="{Binding Path=ShowMoreText, ElementName=thisWindow}"/>
<TextBlock Text="More Text"
Visibility="{Binding Path=ShowMoreText,
ElementName=thisWindow,
Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
</Window>
C#
using System.Windows;
namespace ToggleButtonTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public static readonly DependencyProperty ShowMoreTextProperty =
DependencyProperty.Register("ShowMoreText", typeof(bool),
typeof(MainWindow), new FrameworkPropertyMetadata(true,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public bool ShowMoreText
{
get
{
return (bool)GetValue(ShowMoreTextProperty);
}
set
{
SetValue(ShowMoreTextProperty, value);
}
}
}
}
ElementName must really be an element name. this doesn't fly. Fortunately, you do have an element of type MainWindow here with a ShowMoreText property: the root Window element.
Give the Window a name and use that as ElementName, as below:
<Window x:Class="ToggleButtonTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="200" Height="100"
x:Name="thisWindow">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<StackPanel>
<ToggleButton x:Name="toggleButton" Content="Toggle"
IsChecked="True" Checked="toggleButton_Checked"/>
<TextBlock Text="Some Text"
Visibility="{Binding IsChecked,
ElementName=toggleButton,
Converter={StaticResource BooleanToVisibilityConverter}}"/>
<TextBlock Text="More Text"
Visibility="{Binding ShowMoreText,
ElementName=thisWindow,
Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
</Window>
Note that you can do the same using RelativeSource Self, but I prefer the method above.
The way you have it set up currently won't ever set ShowMoreText to false. The Checked handler will only be called when the ToggleButton's IsChecked changes from false to true. To also go the other way you need a handler for Unchecked as well. The best way to handle this situation would be to instead set a Binding on the ToggleButton that will do both without any event handlers (using Jay's changes):
IsChecked="{Binding Path=ShowMoreText, ElementName=thisWindow}"
Give your Window a name and set the ElementName to that name, instead of using "this".