How to change MainPage TextBlock text from UserControl? - c#

In my Main Page I have some TextBlock and UserControl:
<Page
x:Class="MyProject.MainPage">
<Grid>
<TextBlock x:Name="myTextBlock" Text="" />
<MyUserControl x:Name = "userControl" />
</Grid>
</Page>
How can I get access from userControl class to myTextBlock and change it's text?

Since you're not trying to bind them, there should be a way for your userControl to get access to that TextBlock.
The easiest way can be using a public TextBlock property inside the UserControl and set this property in whether xaml or code behind with the TextBlock.
After than you can easily get TextBlock Text in the property inside your UserControl.
Another option is to use Parnt property of UserControl and try to get all the sibling children there, then picking the desired ones.

Thanks for answers, but I decided to use delegates and Events, and did something like this:
In MyUserControl
private string myString;
public delegate void MyEventHandler(string a);
public event MyEventHandler MyPropertyChanged;
public MyUserControl()
{
this.MyPropertyChanged+= new MyEventHandler(HandlePropertyChanging);
}
private void HandleCreditsChanging(string a)
{
a = myString;
}
In MainPage:
public MainPage()
{
myUserControl.MyPropertyChanged += MyUserControl_MyPropertyChanged;
}
private void MyUserControl_MyPropertyChanged(string a)
{
myTextBlock.Text = a;
}

Related

Linking button controls to data (WPF)

I am working on an application showing 20 graphic buttons controls in a MainWindow (Button1 to Button20).
Each button control can display a Content string, and has a tooltip designed as follow :
<Button x:Name="button1" FontWeight="Bold" FontSize="15" Content="" HorizontalAlignment="Left" Margin="20,69,0,0" VerticalAlignment="Top" Width="92" Height="29" Click="Button_Click" Background="#FFFFFFFF" MouseEnter="button_MouseEnter">
<Button.ToolTip>
<Border Margin="-4,0,-4,-3" Padding="10" Background="Yellow">
<Border.BitmapEffect>
<OuterGlowBitmapEffect></OuterGlowBitmapEffect>
</Border.BitmapEffect>
<Label x:Name ="lbl1" FontSize="20" Content="{Binding Path=ToolTip}">
</Label>
</Border>
</Button.ToolTip>
<Button.Effect>
<DropShadowEffect/>
</Button.Effect>
</Button>
I would like to define the string content and the tooltip string for each button in an XML file so this information can be changed by modifying the XML file.
For this, I created a ViewModel defining an object called Bouton (in french) :
public class Bouton : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void Notify(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
int boutonNumber;
public int BoutonNumber{ get { return boutonNumber; } set { boutonNumber= value; Notify("BoutonNumber"); } }
string texteBouton;
public string TexteBouton { get { return texteBouton; } set { texteBouton = value; Notify("TexteBouton"); } }
string tooltip;
public string Tooltip { get { return tooltip; } set { tooltip = value; Notify("ToolTip"); } }
public Bouton(int nb, string tb, string tt)
{
BoutonNumber = nb;
TexteBouton = tb;
Tooltip = tt;
}
}
When reading the XML file, I create 20 objects of Bouton type with the information about boutonNumber, Content and Tooltip. Then all these Bouton objects are stored into a List collection.
Now I want to use DataBinding between my Bouton list and the graphic controls on my MainWindow to be able to display the content string and the tooltip string on each button.
In the MainWindow, I used the following code :
public MainWindow()
{
InitializeComponent();
List<Bouton> lst = Utilities.CreateList();
this.DataContext = lst;
}
where lst is a List collection correctly initialized.
But I do not know how to make the databinding work on the Button controls. How can I make each of the 20 Button controls correctly link to the corresponding Bouton object (contained in the Boutons collection) ? I mean how can Button1 control get its strings from my Bouton1 object, the Button2 control get its string from Bouton2 object and so on until Button20 control and Bouton20 object ?
Thank you for your help. Please note I am a beginner with WPF and this is my first WPF project with Visual Studio.
I think the simplest option would be to wrap your button in a UserControl.
This UserControl then contains a property of type Bouton (not great naming btw. - even if it is a different language, the term "button" already exists, so this is rather ambiguous. As a non-native english speaker myself, I also recommend getting used to naming in English, it will save you lots of headaches in the long run, but that might be subjective )
You can then bind directly to that property in the UserControl's template. All you have to do is assign each UserControl the correct button data.
eg.
public class CustomButton : UserControl
{
public Bouton ParsedButtonData { get; set; }
public CustomButton()
{
DataContext = this;
InitializeComponent();
}
}
and in your UserControl's template:
<Button ...>
<Button.ToolTip>
<Border ...>
<Label Content="{Binding ParsedButtonData.Tooltip}" ...>
</Label>
</Border>
</Button.ToolTip>
</Button>
You can then place the UserControl in your XAML like this:
xmlns:ctrls="clr-namespace:MyProject.MyNamespace"
<ctrls:CustomButton name="myFirstButton"/>
Now all you have to do is make sure each CustomButton has their ParsedButtonData set to the corresponding piece of data. You can either set this manually for each button created in your XAML, or you can create the CustomButtons through C# in the first place.
If you create your UserControls in XAML, for example:
public void SomeMethod()
{
myFirstButton.ParsedButtonData = lst[0];
}
Alternatively, you might want to look into extending ItemsControl. It's basically made for this sort of application. An ItemsControl has an ItemsSource, which can be any collection type such as List<> or ObservableCollection<>. It then creates its children from that collection, setting the DataContext automatically to the corresponding element in said list.
Examples of that would be DataGrid or ListView. I find that to be a little more involved though, if you really just want to place a bunch of a Buttons on a single View.

How do access the property of the child control?

My English skill is poor because I'm not a native English speaker.
I have created two custom control.
First control has a DP named CaretIndexFromLine and the control name is TextArea.
The following is the summarized cs code of the first control.
public class TextArea : TextBox
{
public int CaretIndexFromLine
{
get { return (int)GetValue(CaretIndexFromLineProperty ); }
set { SetValue(CaretIndexFromLineProperty , value); }
}
public static readonly DependencyProperty CaretIndexFromLineProperty =
DependencyProperty.Register("CaretIndexFromLine", typeof(int), typeof(TextArea), new P
ropertyMetadata(0));
...
}
The second control named Editor has child control TextArea.
The following is the summarized XAML code of the second control.
<Editor> <TextArea x:Name="PART_TextArea"/> </Editor>
And the following is summarized cs code of the second control.
public class Editor : Control
{
public TextArea TextArea { get; private set; }
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.TextArea = (TextArea)Template.FindName("PART_TextArea", this);
}
...
}
And I was going to use the above control in the MainWindow.
<MainWindow> <Editor/> </MainWindow>
There is a problem here.
I want to access the property (CaretIndex) of the TextArea in the XAML code.
I tried like this but It was not operated.
<MainWindow>
<StackPanel>
<Editor x:Name="editor"/>
<TextBlock Text="{Binding ElementName=editor.TextArea, Path=CaretIndexFromLine}/>
</StackPanel>
</MainWindow>
What should I do to achieve the above goal?
Thank you for reading this and please teach me the solution.
Your Editor control has an TextArea property so just set the ElementName to "editor" and the Path to TextArea.CaretIndexFromLine:
<TextBlock Text="{Binding ElementName=editor, Path=TextArea.CaretIndexFromLine}/>
TextArea should be a read-only dependency property though in order for a notification to be raised when it's set in the OnApplyTemplate() method.

WPF change value of a child inside UserControl

I need to change a value from MainWindow of a Control inside my CustomControl.
So lets say I want to change the Labels Content inside UserControl MyControl from MainWindow.xaml.
Example:
<UserControl x:Class="XXXXX.MyUserControl"
.
.
.
>
<Grid>
<Label x:Name="TestLabel"/>
</Grid>
</UserControl>
And in MainWindow.xaml:
<MyUserControl x:Name="TestControl" />
Now how can I access Label.Content from Xaml Designer in MainWindow.xaml?
I didn't find anything out there, so hopefully someone knows how to do that.
Thanks a lot
Expose a custom Property in your UserControl, like below
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
var dpd = DependencyPropertyDescriptor.FromProperty(LabelContentProperty, typeof(MyUserControl));
dpd.AddValueChanged(this, (sender, args) =>
{
_label.Content = this.LabelContent;
});
}
public static readonly DependencyProperty LabelContentProperty = DependencyProperty.Register("LabelContent", typeof(string), typeof(MyUserControl));
public string LabelContent
{
get
{
return GetValue(LabelContentProperty) as string;
}
set
{
SetValue(LabelContentProperty, value);
}
}
}
In xaml of MainWindow
<MyUserControl x:Name="TestControl" LabelContent="Some Content"/>
Added the Following to your UserControl
<UserControl x:Class="XXXXX.MyUserControl"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
.
.
>
Have the User Control Implement INotifyPropertyChanged
Add a Property to the user control like this
Private _LabelText As String
Public Property LabelText() As String
Get
Return _LabelText
End Get
Set(ByVal value As String)
_LabelText = value
OnPropertyChanged("LabelText")
End Set
End Property
Update the Label to Bind from that Property
<Label x:Name="TestLabel" Content="{Binding Path=LabelText}"/>
Then in your MainWindow you can change the property accourdingly
<MyUserControl x:Name="TestControl" LabelText="Testing" />
Then your code behind can also reference that property

Get content of a previous tab on SelectionChanged event

Im trying to get the previous selected tabs content when it is changed to another in a TabControl. For this i subscribe to the SelectionChanged event like so:
tabControl.SelectionChanged += getPreviousData
Then the getPreviousData method looks like this:
private void getPreviousData(object sender, SelectionChangedEventArgs e)
{
e.RemovedItems[0].something
}
Im a little unsure as to how i grab the previous tab content. The previous tab has a textbox control that i need to get the name of, when i change the tab. How can i accomplish that?
Assuming you have a XAML like that
<TabControl x:Name="tabControl" SelectionChanged="tabControl_SelectionChanged">
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5">
<TextBox Width="100" Height="23"></TextBox>
</Grid>
</TabItem>
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5">
<TextBlock x:Name="TextBlock"></TextBlock>
</Grid>
</TabItem>
</TabControl>
First option
Then you can access children of removed TabItem using this code
private void tabControl_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
if (e.RemovedItems.Count != 0)
{
var tabItem = (TabItem)e.RemovedItems[0];
var content = (Grid)tabItem.Content;
var textBox = content.Children.OfType<TextBox>().First();
var text = textBox.Text;
}
}
Second option
You can name your textbox
<TextBox x:Name="TextBoxInFirstTab" Width="100" Height="23"></TextBox>
And access it using his name
var text2 = TextBoxInFirstTab.Text;
Third option
Use MVVM, check this answer MVVM: Tutorial from start to finish?
I am going to provide a simple sample, without any framework, but I suggest you to use anyone, like MVVM Light ToolKit.
Create a View Model
Implement the INotifyPropertyChanged interface
Create a property that will hold your text value, and in the set call the OnPropertyChanged
public class MyViewModel : INotifyPropertyChanged
{
private string _textInFirstTab;
public string TextInFirstTab
{
get { return _textInFirstTab; }
set
{
_textInFirstTab = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Then in your Window constructor, set the DataContext property from Window, to a new instance for your MyViewModel.
public MainWindow()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
Then in your XAML set the Text attribute with a Binding expression
<TextBox x:Name="TextBox" Width="100" Height="23" Text="{Binding TextInFirstTab}"></TextBox>
And in your tabControl_SelectionChanged event, you can access the value like that:
private void tabControl_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
if (e.RemovedItems.Count != 0)
{
var myViewModel = (MyViewModel)DataContext;
var text = myViewModel.TextInFirstTab;
}
}
If it is switching between existing tabs which you are after, then I would suggest simply storing the index of the selected tab in a class variable.
Sample code looks like this:
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
// variable to store index of tab which was most recently selected
private int lastSelectedTabIndex = -1;
public Form1()
{
InitializeComponent();
// initialise the last selected index
lastSelectedTabIndex = tabControl1.SelectedIndex;
}
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
// sanity check: if something went wrong, don't try and display non-existent tab data
if (lastSelectedTabIndex > -1)
{
MessageBox.Show(string.Format("Previous tab: {0} - {1}", lastSelectedTabIndex, tabControl1.TabPages[lastSelectedTabIndex].Text));
}
// store this tab as the one which was most recently selected
lastSelectedTabIndex = tabControl1.SelectedIndex;
}
}
}
This was written and tested in a simple application with one form and a TabControl added. No changes were made to the default properties.
You will, of course, have to hook into the event handler. I did so by double-clicking it in the IDE, but you could also hook in manually by adding:
this.tabControl1.SelectedIndexChanged += new System.EventHandler(this.tabControl1_SelectedIndexChanged);
to the form constructor, called "Form1()" in the example code.
Getting the name of a textbox is an unusual thing to want to do. May I ask what you are trying to achieve? There's probably a better way to do it than trying to determine the name of a control.

How can I modify from C# the value of an element which is define in a XAML DataTemplate?

I created a "WPF Application Project" in Visual Studio 2013.
I opened the "MainWindow.xaml" file and I wrote the following XAML code:
<Window x:Class="TestProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="AlphaDataTemplate">
<Label
Name="LabelInDataTemplate"
Content="Good morning!" />
</DataTemplate>
</Window.Resources>
<Grid>
<ContentPresenter
Name="MyContentPresenter"
ContentTemplate="{StaticResource AlphaDataTemplate}" />
<Button
Name="MyButton"
Click="MyButton_OnClick"
Content="Change the content of the Label in the DataTemplate"
Width="320"
Height="30" />
</Grid>
In this XAML file I created a "DataTemplate" which corresponds to the key "AlphaDataTemplate". The DataTmplate contains just one label with the name "LabelInDataTemplate" where I have hardcoded the "Good morning!" string in the "Content" attribute of the label.
Then I use created a "ContentPresenter" with the name "MyContentPresenter" and I pass as content the "DataTemplate" I previously created (AlphaDataTemplate).
As next step, I created a "Button" with the name "MyButton" and I have set a "Click" event called "MyButton_OnClick"
So far so good...!
The question comes now and actually in C# in the code behind file "MainWindow.xaml.cs". See the code below:
using System.Windows;
namespace TestProject
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void MyButton_OnClick(object sender, RoutedEventArgs e)
{
LabelInDataTemplate.Content = "Bye!"; // <-- Tha does not work.
}
}
}
In this C# code behind file you can see the definition of the "Click" (MyButton_OnClick) event of the Button (MyButton) which appears in XAML.
What I am trying to do in this "Click" event, is to change the value of the "Content" of the "Label" (LabelInDataTemplate) which is in the DataTemplate (AlphaDataTemplate).
Unfortunately, that does not work.
I cannot actually access the "Name" (LabelInDataTemplate) of the "Label", because it is contained in the "DataTemplate" (AlphaDataTemplate)
If anyone has any idea, how could I modify from C# the value of an element which is define in a XAML DataTemplate, please give me feedback. I would really appreciate it.
Thank you in advance.
I strongly oppose your method of changing the content of label via DataTemplate, However your requirement is possible, but very subtle.
Code
private void MyButton_OnClick(object sender, RoutedEventArgs e)
{
var alphaDataTemplate = this.Resources["AlphaDataTemplate"] as DataTemplate;
var label = alphaDataTemplate.FindName("LabelInDataTemplate", MyContentPresenter) as Label;
label.Content = "It Works";
}
Please learn MVVM and use proper DataBinding for this purpose. For sake of solving this problem:
Implement INotifyPropertyChanged interface on your Window class and Define string property like below
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public string _contentMsg;
public string ContentMsg
{
get { return _contentMsg; }
set
{
_contentMsg = value;
RaisePropertyChanged("ContentMsg");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propName)
{
if(PropertyChanged !=null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
In your xaml bind the ContentPresenter and update your DataTemplate label like
<ContentPresenter
Name="MyContentPresenter"
Content = "{Binding ContentMsg}"
ContentTemplate="{StaticResource AlphaDataTemplate}" />
<DataTemplate x:Key="AlphaDataTemplate">
<Label
Name="LabelInDataTemplate"
Content="{Binding}" />
Now in click handler (I would use Commands here), set ContentMsg to whatever you want
private void MyButton_OnClick(object sender, RoutedEventArgs e)
{
ContentMsg = "Bye!";
}

Categories

Resources