Access XAML controls from C# code-behind - c#

I have two files in my VS project: Custom.xaml and Custom.cs
In my XAML file, I have the following text boxes:
<TextBox x:Name="TextBox1" Text="{Binding Value, Mode=TwoWay}" Foreground="Black" Background="Green" SelectionChanged="TextBox1_SelectionChanged" />
<TextBox x:Name="TextBox2" Text="{Binding Value, Mode=TwoWay}" Foreground="Black" Background="Green" SelectionChanged="TextBox2_SelectionChanged" />
In my .cs, I have the following method:
void TextBox1_SelectionChanged(object sender, RoutedEventArgs e)
{
TextBox t = e.Source as TextBox
}
I can successfully hit the event handler above. Then, I can grab TextBox1 and it's properties by using e.Source, but I would like to access TextBox2 and it's properties.
As a sidenote, the .cs file is just a C# class that I am referencing, not a xaml.cs. Additionally, I understand that I could implement this via a UserControl, but cannot do that in this scenario for reasons that are outside the scope of this post.
Please advise on how I can get/set properties of TextBox2.
Thanks.
EDIT: Any other input on this? As a workaround, I've added an event handler called TextBox2_Loaded, and then set e.Source to an instance variable. Then, in TextBox1_SelectionChanged, I can access the instance variable. Would really like to just target the control directly (ex. TextBox2.IsEnabled). I must be missing a declaration or inheritance somewhere. Can't even find the control using FindName.

Alright, so I apparently had left out a critical component in this post... My TextBox controls are inside of DataTemplate controls. From my research, the TextBox controls cannot be accessed when inside of DataTemplate controls. I really didn't think that would matter, but I guess the instance variables are not created when this scenario exists.
If I've interpreted this incorrectly, please provide input. For now, I've gone ahead and added a Loaded event and defined my TextBox controls as instance variables so that I can access them and change properties when other activities occur.
Thanks for everyone's input.

As long as you have set a namein the XAML, you can access it directly by name (The XAML compiler will create an instance variable for you.)
void TextBox1_SelectionChanged(object sender, RoutedEventArgs e)
{
TextBox t = e.Source as TextBox
TextBox2.Text = "Whatever";
}

This just happened to me. I had to close my solution and reopen it.

Related

Add EventListener to WPF control defined in Generic.xaml [duplicate]

I have a WPF control ParentWPFControl from a third party that I would like to inherit from (let's call the child class ChildWPFControl). In the process, I plan to override some of the back-end logic and parts of the front end styles. I can do the former just fine but I have problems doing the latter.
I attempt to use a xaml <-> xaml.cs structure for the child country, but that appears to be not allowed with the following warning from VS:
Partial declarations of 'ChildWPFControl' must not specify different base classes
Now, I suppose I can write a ResourceDictionary XAML and define the front end there, but that becomes a problem if I want to add event handlers to the XAML (at least I couldn't find a way to do that)
Another alternative I have is to define the override template directly in the objects that use the ChildWPFControl but that makes the design less modular.
A final alternative I can think of is to make a xaml <-> xaml.cs pair that is a XAML style container and then force the ChildWPFControl to use the ControlTemplate defined within through the back end event handler.
Anyway, what I am looking for is an elegant and modular solution for my problem. Any advice would be welcomed.
Thanks
There are a couple of steps necessary to completely override a WPF Control. Some are necessary some are optional depending on your needs. I will explain the two important ones for you:
Creating a new default style
Every WPF control has somewhere a default style which contains it visual representation and override properties. Now if you derive from control WPF still thinks you want to use this default style, to change that you change the DefaultStyle in a static constructor like this
class MyButton : Button
{
static MyButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyButton), new FrameworkPropertyMetadata(typeof(MyButton)));
}
}
Now if you use MyButton WPF tries to find a Style for MyButton, not for Button. OverridesDefaultStyle is a property in a style which might also be handy at some points. Usually these default styles should be placed in a theme related xaml.
Event Handlers when overriding classes
It is correct in a ControlTemplate or Style you can't use the syntactic sugar of using event like Click="OnClick". The point is, that the visual representation is decoupled from the logic part as much as possible. There are other ways though to overcome this, using the OnApplyTemplate method. By overriding this you ask the template "Give me this control" and then you just add your events there.
override OnApplyTemplate()
{
var innerChild = Template.FindName("PART_InnerChild", this) as MyInnerControl;
if(innerChild != null)
innerChild.SomeEvent += OnSomeEvent;
}
Note: The name of these controls usually begin with a PART_ by convention, this can be seen in WPF basic controls aswell. Its a nice way to tell the designers "Without this control, the logic part might break". There is also the attribute TemplatePart but it is not really important, WPF doesn't care about it. AFAIK Expression blend does some with it, personally i use it to tell other people what kind of inner controls are absolutely necessary to make this control work.
Personal advice
Deriving from a class is usually the last step we do when trying to customize controls. Because a lot of work is necessary to fully make it work and it can be limiting in reusability, we try to avoid it, for example a good alternatives are besides template overriding and styling; attached behaviors.
Lastly,
The whole subject is covered in a nice MSDN article.
Hope that helps
You can create your user control as wrapper, containing base control. In this way you can change styles in xaml add some logic in C# for wrapped contrŠ¾l. But it's tediously process.
Edit:adding sample(wrapper for telerik:RadComboBox )
XAML:
<UserControl x:Class="Controls.SingleDictionaryValueSelector"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:CardControls="clr-namespace:Controls"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation" MinWidth="150" MinHeight="25" >
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="25"></ColumnDefinition>
</Grid.ColumnDefinitions>
<!-- customize visual for wrapped control -->
<telerik:RadComboBox x:Name="cb"
Grid.Column="0"
VerticalAlignment="Center"
SelectedValuePath="Key"
ClearSelectionButtonContent="Clear"
ClearSelectionButtonVisibility="Visible"
CanAutocompleteSelectItems="True"
CanKeyboardNavigationSelectItems="True"
SelectAllTextEvent="None"
OpenDropDownOnFocus="false"
IsFilteringEnabled="True"
TextSearchMode="Contains"
EmptyText="Select item"
telerik:StyleManager.Theme="Metro"
FontFamily="Calibri"
FontSize="14"
IsEditable="True"
Foreground="#666"
KeyDown="cb_KeyDown"
SelectionChanged="cb_SelectionChanged"
GotMouseCapture="cb_GotMouseCapture"
DropDownOpened="cb_DropDownOpened"
KeyUp="cb_KeyUp">
<telerik:RadComboBox.ItemTemplate>
<DataTemplate>
<TextBlock TextWrapping="Wrap" Width="{Binding RelativeSource={RelativeSource AncestorType=telerik:RadComboBox},Path=ActualWidth}" Text="{Binding Path=Value}" />
</DataTemplate>
</telerik:RadComboBox.ItemTemplate>
</telerik:RadComboBox>
<CardControls:ErrorInfo x:Name="errorInfoControl" Grid.Column="1" Visibility="Hidden"></CardControls:ErrorInfo>
</Grid>
</UserControl>
CS:
public partial class SingleDictionaryValueSelector : IMyCustomInterface
{
....
private void cb_KeyDown(object sender, KeyEventArgs e)
{
RadComboBox senderCombo = sender as RadComboBox;
...
}
private void cb_KeyUp(object sender, KeyEventArgs e)
{
SearchExecute();
}
private void cb_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
RadComboBox senderCombo = sender as RadComboBox;
...
}
private void cb_DropDownOpened(object sender, EventArgs e)
{
...
}
...
}
It looks like you have your inheritance mixed up more than that it is not allowed. Your root element of your xaml must match the base class of your xaml.cs.
If you are defining the base class in the same project, you will not be able to use it as the base class in the xaml, because it itself is still xaml and not a compiled control yet. Some ways to solve this: You can compile it in a seperate project and reference it, you can compile the base class entirely in .cs instead of a partial class, or you can use some style wizardry. Here is a link with examples of the last two: http://svetoslavsavov.blogspot.ca/2009/09/user-control-inheritance-in-wpf.html

WPF uses and expected results of AccessText class

I was going through the class AccessText today. I couldn't identify exact use and results of using this class.
If you use AccessText with a Label and use Target property as a
TextBox, the TextBox will get focus when access key is pressed.see below code:
<StackPanel>
<Label Target="{Binding ElementName=txtbox}">
<AccessText>_first_second</AccessText>
</Label>
<TextBox Name="txtbox" Width="100" Height="50"/>
</StackPanel>
If you use AccessText with a Label and use Target property as a
Buton , the Button Click event will get executed when access
key is pressed.
So my questions are
1.what is the definitive behavior of AccessText Class? If I have to predict it's uses with other type of controls like DataGrid, ComboBox,
RadioButton? how can i be sure of expected result?
2.Why is this class derived from FrameworkElement? What applications does it have as a FrameworkElement? seems a bit more for
just specifying Accesskeys etc.
AccessText is a FrameworkElement that acts more or less like a special type of TextBlock that allows any keyboard character following a single underscore (_) to act as an access key.
For a given control, the behavior of associated access keys depends on its OnAccessKey method. OnAccessKey is a virtual method of UIElement, which provides the following definition:
protected virtual void OnAccessKey(AccessKeyEventArgs e)
{
this.Focus();
}
So, any control that doesn't override the definition of OnAccessKey defined by UIElement will maintain the default behavior, which is for the control to be brought into focus when the access key is pressed.
ButtonBase, which Button inherits from, has the following definition for OnAccessKey:
protected override void OnAccessKey(AccessKeyEventArgs e)
{
if (e.IsMultiple)
base.OnAccessKey(e);
else
this.OnClick();
}
So the default behavior of Button and other controls that inherit from ButtonBase will be to bring the control into focus if IsMultiple is true, otherwise, it will raise the click event. (IsMultiple is true if an access key is associated with more than one UIElement.)
With this background in mind, here are the answers to your specific questions:
The definitive behavior of an AccessText element used as a control's ContentPresenter is to register the first letter following a single underscore with the AccessKeyManager, which will invoke the control's OnAccessKey method when the key is pressed. Knowing what this will do for a particular control requires knowing which definition of OnAccessKey is in force for that control. If there are no overrides in its inheritance chain, pressing the access key will bring the control into focus. If there is an override, the behavior will depend on the overriding method's definition. This can be determined via experimentation, reading relevant documentation, or examining the source code.
AccessText is a FrameworkElement for the same reasons that TextBlock is a FrameworkElement. It has a visual form and takes up space that the layout system needs to take into account when positioning other elements relative to it. Also, FrameworkElements allow for styling, and they possess their own DataContext property, which allows for binding scenarios that would otherwise not be possible. If AccessText were not a FrameworkElement, it would be unnecessarily limiting and prevent reasonable (though perhaps rare) use cases WPF developers may have.
Edit
Here's an example of a fancy power button that demonstrates the usefullness of AccessText being a FrameworkElement:
<Grid>
<Button Width="150"
Height="35"
Command="{Binding PowerCommand}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Status" />
<Rectangle Margin="5,2,0,0"
Width="10"
Height="10"
Fill="{Binding PowerFill}" />
<AccessText Margin="25,0,0,0"
Text="{Binding PowerText}" />
</StackPanel>
</Button>
</Grid>
This results in (after pressing Alt):
After clicking the button, or pressing Alt+S, the view model would respond to the command by changing the Text and Fill, resulting in this:
Clicking or using the access key again would return to the first state.

How to set and get tag in xaml dynamically?

While creating GUI using xaml, I created a textbox with a tag like this:
<TextBox Name="TextBox" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="216,178,143,120" Width="158"
Tag="myTag"/>
Now I want to let the user be able to change this tag. For that, I am looking for a kind of function of form:
TextBox.SetTag( "User Provided Tag" )
So that the tag can be changed into this one:
<TextBox Name="TextBox" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="216,178,143,120" Width="158"
Tag="User Provided Tag"/>
After searching the internet for quite a while, I didn't come up with any practical solution. Could someone help? Thanks.
You can use a binding between two controls. Say a user is allowed to enter a tag value from a TextBox. You just bind the Tag of the second TextBox to a Text property of the first TextBox:
<TextBox Name="enterTagTextBox" />
<TextBox Name="getTagTextBox" Tag="{Binding ElementName=enterTagTextBox, Path=Text}"/>
To test it I added a Button in my XAML:
<Button Height="25" Click="Button_Click_1"/>
In code behind I just retrieve the tag value and display it like this:
private void Button_Click_1(object sender, RoutedEventArgs e)
{
string text = this.getTagTextBox.Tag.ToString();
global::System.Windows.Forms.MessageBox.Show(text);
}
I think your problem may arise from you naming your TextBox, "TextBox". Try giving it a name that does not clash with the class name, like "txtMyTextBox".
Then you can do, txtMyTextBox.Tag = "User Provided Tag";.
Or you can bind to it as PiotrWolkowski suggests.
However, I would like to add, that it seems like there should be a cleaner way of achieving your desired behaviour. With the caveat that I don't know the details of what you are trying to implement.
I strongly suggest creating a ViewModel (see: MVVM Pattern) to hold the data you want users to be able to edit. Then use the bindings in WPF to display the data.
You would need to use the property element usage in order to set the Tag property in Extensible Application Markup Language (XAML) to anything other than an object with a known and built-in type converter, such as a string.

WPF checkbox Tag is null eventhough it is set in XAML

I am trying to make use of the Tag property of the WPF checkbox. This is my XAML:
<CheckBox Content="BTC/USD" HorizontalAlignment="Left" Margin="14,0,0,41"
VerticalAlignment="Bottom" IsChecked="True" Checked="CheckBox_Checked" Tag="btcusd"
Unchecked="CheckBox_Checked"/>
When I open my app, CheckBox_Checked is called immediately, but the sender's Tag property is null. Why can this happen?
The Checked property is set right on the XAML loading, when you set IsChecked="True". The tag may be loaded only later when the XAML loading code decides to set this property. That's why you can see uninitialized properties.
simple Solution for all of these type bugs/errors:
1- bool bFormLoaded;//=false ;
2- at [YourWinOrControlorWPF]_Loaded(object sender, RoutedEventArgs e)
add this flag at end of function:
bFormLoaded=true;
3-at UseDefaultFoldersCB_Checked(...)
add this line
if(bFormLoaded==false) return;
100%

WPF Combobox with clear button

I am trying to create a WPF ComboBox control which contains a clear button when something is selected. The control should have two states: if something is selected the control looks like a label with a clear button. If nothing is select then a normal ComboBox is display. The two states are showing in the picture below.
Researching my problem I came across the following SO questions which are very similar to mine:
Adding a button to a combobox in wpf
How to subclass a WPF ComboBox to add an extra button
Both suggest subclassing the ComboBox providing a modified template with the extra button. But this is where I am a tad confused. The answer by John Bowen to the second linked question indicates that I should copy the ComboBox's default template; modifying it to include a button getting the template from Blend. Not being proficient with blend I found the template on MSDN here:
http://msdn.microsoft.com/en-us/library/ms752094(v=vs.85).aspx
My problem is I am not quite sure what I should change. Looking at the Default template I think I need to do something along the lines of:
Create a new 'IsSelected' property which I can hook triggers to.
Add a control template for a Clear Button with trigger attached to IsSelected which hides the button.
Attached a IsSelected Trigger to the ComboBoxToggleButton control template to hide it upon selection.
Somehow re-size the PART_EditableTextBox textbox in the ComboBox Template when IsSelected is true.
Does this seem right and any pointers on how I do it or other suggestions if I am barking up the wrong tree.
Maybe you will accept a simpler solution - just place a TextBlock with a Button above your ComboBox?
The xaml will look like this:
<Grid>
<ComboBox ItemsSource="{Binding ...}" x:Name="cbox"/>
<Grid Background="Gray" Visibility="{Binding SelectedItem, ElementName=cbox, Converter={StaticResource NullItem2Visibility}}">
<TextBlock Text="{Binding SelectedItem, ElementName=cbox}" HorizontalAlignment="Left"/>
<Button Content="Clear" HorizontalAlignment="Right" Click="ClearItem"/>
<Grid>
</Grid>
Codebehind will have method ClearItem:
public void ClearItem(object sender, EventArgs e){
cbox.SelectedItem=null;
}
And the converter to show and hide textblock and a button:
class NullItem2Visibility:IValueConverter{
public object Convert(object value, Type type, object parameter, CultureInfo i){
return value == null ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(...){...}
}

Categories

Resources