Xaml TextBox not behaving - c#

I am trying to create UI from XAML at runtime, with something like
private void Application_Startup (object esender, StartupEventArgs e)
{
this.RootVisual = (UIElement)(XmlReader.Load(e.InitParams["Xaml"])
If I feed it this XAML:
<Canvas
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sdk="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls">
<StackPanel>
<TextBox Width="120" Margin="8" Text="Edit Me" />
<CheckBox Margin="8">Normal</CheckBox>
<ComboBox Margin="8" Width="120" SelectedIndex="1">
<ComboBoxItem Content="First Normal Item" />
<ComboBoxItem Content="Second Normal Item" />
</ComboBox>
</StackPanel>
</Canvas>
then the check box and list behave as expected, but my TextBox does not respond to typing, it stays with its initial value.
The really weird (to me) part is that if I put a handler for KeyDown on to the RootVisual, and in there display a message box, it works. If I have an empty handler or no handler it doesn't.
Do I need to set up some explicit handling for some events? If so, which ones, and how do I handle them?
Upadate: as suggested, I tried putting the dynamic markup into the MainPage of a new app, like this:
public MainPage()
{
InitializeComponent();
var dynamic = XamlReader.Load(xaml);
this.LayoutRoot.Children.Add(dynamic as UIElement);
}
where xaml is a string literal containing the content as above, and everything else is just how VS2010 wizard left it. That works. But I can't see what the effective difference is.
Update update: that's a red herring; the difference is the environment. It works in VS, but not in the Silverlight ActiveX control that I am using in the real app.

Did you define the root namespace on your root element?
<param name="initParams" value="xaml=<TextBox xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' Text='hi'/>" />
Just a shot in the dark here, but have you tried adding the dynamically created content as the child of a static "MainPage.xaml" Grid instead of as RootVisual?

Check for IsEnabled="True" property in your main XAML file, if it is set to false then controls will not be editable.

Related

WPF ContextMenu = {x:Null} but still shows menu inside ContentControl

I need disable standard ContextMenu of TextBox. I've created a new WPF project and added the following:
<Window x:Class="WpfApplication3.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">
<Grid>
<ContentControl>
<ContentControl.ContentTemplate>
<DataTemplate>
<TextBox ContextMenu="{x:Null}" VerticalAlignment="Top" HorizontalAlignment="Left" Width="50"></TextBox>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
</Window>
But this is what i get :
The following code works fine :
<Grid>
<TextBox ContextMenu="{x:Null}" VerticalAlignment="Top" HorizontalAlignment="Left" Width="50"></TextBox>
</Grid>
Why is this happening?
Update.
According to the accepted answer I've created a class derived from TextBox in order to be able to show parents ContextMenu.
public class TextBoxNoMenu: TextBox
{
public TextBoxNoMenu()
{
ContextMenu = null;
}
}
Why is this happening?
This is an interesting case of a control's behavior changing depending on where/how a property is set.
TextBox provides its own context menu by default. The only time it won't do this is when you explicitly set the local value of ContextMenu to null. This is what happens in your simple example where the TextBox is directly within in the Grid.
However, when you set a property inside a template, you're not actually setting a local value; you're setting a "parent template" value. If you inspect the value with DependencyPropertyHelper.GetValueSource(), you'll see the base value source is ParentTemplate instead of Local. Thus, the menu still gets overridden.
See Dependency Property Value Precedence for more information about the different kinds of dependency property value sources.
#OmegaMan's suggestion of assigning a 'hidden' context menu seems to work pretty well.
Note that while you mayhave disabled the ContextMenu on TextBox, if it's in another control, you may actually be seeing the ContextMenu of such a wrapper. Try Snooping it to see more specifically this sort of behaviour.
Note also that many of the default Control Templates throughout WPF can cause issues such as these by adding their own child objects. Seeing the default template for TextBox uses a Border and then <ScrollViewer Margin="0" x:Name="PART_ContentHost" />, you're likely seeing the ContextMenu of a child object if TextBox.
This seems to be a running issue where X:Null does not 'turn off' the default context menu. A better way would be to change it's visiblity:
<TextBox.ContextMenu>
<ContextMenu Visibility="Collapsed"/>
</TextBox.ContextMenu>
I had a similar issue, but I was generating my controls programmatically, and my parent control is a dockpanel. Based on the accepted answer, I decided to set the null value in the code behind.
<Grid>
<DockPanel>
<TextBox Name="txtBox" VerticalAlignment="Top" HorizontalAlignment="Left" Width="50"></TextBox>
</DockPanel>
</Grid>
and then
private void Window_Loaded(object sender, RoutedEventArgs e)
{
txtBox.ContextMenu = null;
}
EDIT: I felt this was kind of a haphazard answer, as it doesn't fully or directly solve this question. I did some digging and if you implement the method found in the answer to This Question you can find the textbox in the code-behind.
So, if you have this
<Grid>
<ContentControl>
<ContentControl.ContentTemplate>
<DataTemplate>
<TextBox Name="txtBox" VerticalAlignment="Top" HorizontalAlignment="Left" Width="50"></TextBox>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
Then you should be able to find your textbox by name (txtBox in this case) and set the context menu to null
TextBox myTextBox = FindChild<TextBox>(Application.Current.MainWindow, "txtBox");
myTextBox.ContextMenu = null;
Personally I'd prefer this to creating a new class with inheritance, but whatever works for you. This still doesn't answer "Why is this happening?" but I think the accepted answer does a good job of that.

How to prevent a UserControl from taking focus away from its parent?

I am using MVVM for my application and have a form that allows the user to enter basic personnel information. The form includes a UserControl which is, basically, an ItemsControl that includes textBoxes that can be dynamically created. This is a simplified version:
<ItemsControl x:Name="items" ItemsSource="{Binding MyItemsCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid x:Name="row">
<TextBox x:Name="textBox" Text="{Binding ContactInfo, ValidatesOnExceptions=True}" extensions:FocusExtension.IsFocused="{Binding IsFocused}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button x:Name="NewItemButton" Command="{Binding AddItemToMyCollectionCommand}" />
I want the TextBox that has just been created to receive focus, therefore I added an attached property. This is part of it:
public static readonly DependencyProperty IsFocusedProperty =
DependencyProperty.RegisterAttached("IsFocused", typeof(bool), typeof(FocusExtension), new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));
private static void OnIsFocusedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var uie = (UIElement)d;
if ((bool)e.NewValue)
{
uie.Focus();
}
}
In the form that contains the UserControl there are several other text boxes before and after. The UserControl has its own ViewModel, which I set as the DataContext of the control through a property in the container's ViewModel. Basically, a simplified version of the container looks like this:
<StackPanel Orientation="Horizontal" />
<TextBox x:Name="firstName" />
<TextBox x:Name="lastName" />
<local:DynamicFormUserControl
x:Name="phones"
DataContext="{Binding PhonesViewModel}" />
<local:DynamicFormUserControl
x:Name="emails"
DataContext="{Binding EmailsViewModel}" />
<TextBox x:Name="address" />
</StackPanel>
My problem is that I want the firstName TextBox to get the focus when the form is loaded for the first time, but the form keeps on placing the focus on the first TextBox of the phones UserControl. I tried to override it by using firstName.Focus() on the Loaded event of the form, but this didn't work, and no matter what I tried the focus is still on the phones userControl instead of the first element in the form that contains it.
Does anybody have any idea how to solve this?
Thanks.
Here you go
add FocusManager.FocusedElement="{Binding ElementName=firstName}" to your stack panel
<StackPanel Orientation="Horizontal"
FocusManager.FocusedElement="{Binding ElementName=firstName}"/>
<TextBox x:Name="firstName" />
<TextBox x:Name="lastName" />
<local:DynamicFormUserControl
x:Name="phones"
DataContext="{Binding PhonesViewModel}" />
<local:DynamicFormUserControl
x:Name="emails"
DataContext="{Binding EmailsViewModel}" />
<TextBox x:Name="address" />
</StackPanel>
also notice that you may need to prevent items control in the user control from focusing itself
<ItemsControl x:Name="items" Focusable="False" >
<ItemsControl.ItemTemplate>
I guess I managed to find a solution. The problem was that the form I created was itself a user control inside a window, and never got focus. I didn't think that would be relevant so I didn't mention it in my previous post- sorry. I found in this solution for forcing focus to a user control.
Basically, when I have a UserControl inside a window it doesn't get focus even if I try to set the focus with either Focus() or FocusedElement. So to overcome this problem I found on a different post a workaround. Basically I added it to the code-behind of the UserControl that contains the firstName TextBox. If we call the UserControl, say, PersonalInfoUserControl, the constructor of the control would look like this:
public PersonalInfoUserControl()
{
InitializeComponent();
this.IsVisibleChanged += new DependencyPropertyChangedEventHandler(UserControl_IsVisibleChanged);
}
I added an event handler to the IsVisibleChanged event of the control. The method would look like this:
void UserControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue == true)
{
Dispatcher.BeginInvoke(
DispatcherPriority.ContextIdle,
new Action(delegate()
{
firstName.Focus();
}));
}
}

Is this concept of a button containing a textbox possible?

Recently I had been looking for a way to make the tabs in a TabControl editable and came across This example on telerik's website. That did exactly what I wanted but it got me thinking about a similar usage for buttons. I was wondering if it would be possible to use something like that and make a button that would show a textbox instead of the content presenter when say, you right click the button? I tried to make something like this work but so far have only ended up with a blank button.
<Button x:Name="SB" Height="222" Width="222" Click="SB_Click">
<ContentControl.ContentTemplate>
<DataTemplate>
<local:SuperButton Content="{Binding Path=x, Mode=TwoWay}"/>
</DataTemplate>
</ContentControl.ContentTemplate>
</Button>
Where x is a string variable and using the code behind from the link above (with a class name change, of course).
edit: This button will be in an itemscontrol, so I don't think naming the inner elements in xaml will work, but I do like the ease of Wolfgang's answer.
The WPF Content Model is really flexible and allows literally anything inside anything.
This is perfectly valid XAML:
<Button>
<TextBox/>
</Button>
Or even:
<Button>
<MediaElement Source="C:\Videos\WildLife.wmv"/>
</Button>
You can simply host a (e.g.) label (TextBlock) with the text AND a TextBox inside the Button and set their Visiblity properties.
That way, if you right click the button, the TextBox shows up.
<Button>
<Grid>
<TextBox Text=normal button caption" x:Name="label" />
<TextBox
x:Name="textbox"
Text="visible on right click"
MouseRightButtonDown="HandleRightClick"/>
</Grid>
</Button>
And then in your C# code create an event handler to set the Visiblity correctly.
void HandleRightClick(object sender, MouseButtonEventArgs e)
{
label.Visibility = Visibility.Collapsed;
textBlock.Visibility = Visibility.Visible;
}

send string from usercontrol to appliucation page

I need to send a string(TextBlockName.Text) from usercontrol to application page after clicking on the button.
Application page XAML:
<ListBox x:Name="lstFlags">
<ListBox.ItemTemplate>
<DataTemplate>
<local:ListItem />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
UserControl "ListItem" с# code:
public partial class ListItem : UserControl
{
...
private void Button_Click(object sender, RoutedEventArgs e)
{
}
}
User control Xaml:
<Button Click="Button_Click">
<TextBlock Name="TextBlockName" Text="{Binding ShortName}" />
</Button>
So. I think it is necessary to generate event in application page when button clicked. How can I do this?
I think what you're trying to ask here is how you can send the textblock1.Text (for example) value to the code behind.
By the looks of it, you're using Data Binding to bind the data, but you also have a Button_Click event.
Generally I use one or the other (unless i'm doing something complex), if you simply want to get the Text value form the TextBlock, then you can either do:
<TextBlock Name="textBlockName" Text="{Binding ShortName}" Mode="TwoWay">
The Mode="TwoWay" ensures that the value of the Text Block gets sent to and from the code-behind object, in this case calledShortNameand to theTextBlock.Text`.
The other method is to simply create a Button click event which you already seem to have. In the Button Click event, simply do the following:
string myString = textBlock1.Text;
As you can guess, that simply gets the string value that's within the Text property of the textBlock and puts it inside the myString object.
But - as an important note, you should try to elaborate as much as possible to ensure that people who are viewing your question understand and can help you.

Using Icons in WPF Database Driven Application Results

I am attempting to make a WPF application. The application needs to use a "list view" to show results of queries to the database. I have been able to successfully create the application (GUI, database, LINQ, etc.), however, the display of my query results appear more "gridlike".
The specifications for the project below show that each record that appears in the results needs to have a green circle icon next to it. I have removed the actual results from the images below to keep the contents of the database private.
I don't have enough Reputation Points to post images, so I posted pictures so a sample/testing domain that I use. You can see screenshots here of the WPF app and code here:
http://digitalworkzone.com/WPF.html
What am I doing incorrectly? Is there something I need to add or modify to my code to be able to get the green circles and more of a "list" style to display my query results?
Understand the WPF content model. http://msdn.microsoft.com/en-us/library/bb613548.aspx
Anything that has a 'Content' property basically behaves in two ways. If the 'Content' is set to something that derives from UIElement, then the class will manage it's own presentation. Anything else, however, will just get .ToString() called, and it's text displayed instead.
What this means in the long run is that everything in WPF can display anything. If you want to show a button in a button, you can. For example:
<Button>
<Button.Content>
<Button Content="This will show as text" />
</Button.Content>
</Button>
The inner button will have text, but the outer button will show a Button because Button derives from UIElement and therefore will handle its own presentation.
In your picture examples above, you have ListBoxes/DataGrids that you want to fill in with graphical information. Try this out:
<ListBox HorizontalContentAlignment="Stretch">
<ListBox.Items>
<Button Content="One"/>
<Button Content="Two"/>
<Button Content="Three"/>
<Button Content="Four"/>
</ListBox.Items>
</ListBox>
Now you have a ListBox that shows Buttons instead of Text. You can take this a step further and contain the items in a stackpanel, for example:
<ListBox HorizontalContentAlignment="Stretch">
<ListBox.Items>
<StackPanel Orientation="Horizontal">
<Button Content="A button"/>
<Label Content="Some text" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button Content="A button"/>
<Label Content="Some text" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Button Content="A button"/>
<Label Content="Some text" />
</StackPanel>
</ListBox.Items>
</ListBox>
Now we have items that contain a layout container (StackPanels, which then contains other elements).
However, if you set the ItemsSource elsewhere, you can actually use a DataTemplate to display the contents. A DataTemplate in effect targets a particular class and lays out it's contents as defined in XAML. Consider:
Code Behind:
public partial class MyWindow : UserControl {
public MyWindow() {
InitializeComponent();
MyListBox.ItemsSource = new List<Person> {
new Person("Sam", "Smith"),
new Person("Jim", "Henson"),
new Person("Betty", "White"),
};
}
XAML:
<ListBox HorizontalContentAlignment="Stretch" x:Name="MyListBox" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<Label Content="{Binding FirstName}"/>
<Label Content="{Binding LastName}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now when the Listbox displays, it will cycle through each of the items in the ItemsSource property, and then lay them out using the DataTemplate. It's possible to have the DataTemplate target specific classes by using the DataType property if you're using polymorphism (as in different types of people such as 'Cusomters' or 'Employees' which all derive from 'Person).
The problem with this approach is that you are setting the value of the items directly, which is bad form. It's better to define a class that handles all of the data for your view separately. Consider:
public class ViewModel {
// WPF will automatically read these properties using reflection.
public List<Person> People {
get {
return new List<Person> {
new Person("Sam", "Smith"),
new Person("Jim", "Henson"),
new Person("Betty", "White")
};
}
}
}
That will hold all the data for the view, now let's add it to the actual window. First we need to reference the namespace ('xmlns' means xml namespace):
<Window x:Class="Sharp.MyWindow"
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:lol="clr-namespace:Sharp">
The namespace is Sharp (the namespace where my stuff lives), and the alias we'll give it is lol. Now we attach our ViewModel class to the window by setting it to the DataContext property, as in:
<Window>
<Window.DataContext>
<lol:ViewModel />
</Window.DataContext>
</Window>
This makes all of the public properties on the ViewModel class available to the Window. This way, if we want to read the Persons information into our ListBox, we simply say:
<ListBox HorizontalContentAlignment="Stretch" ItemsSource="{Binding People}" >
...
</ListBox>
Notice that we say ItemsSource={Binding People}, which means 'scan the ViewModel for any public properties called 'People' and then retrieve those results. This is essentially the fundamentals behind the MVVM approach. You might have all of your business logic in one or many classes which handle the main application operation in a Model, but then you have a ViewModel which interacts with the Model and exposes the results as public properties. WPF automatically binds to those properties and presents them for your. The information just flows, rather than setting the values by force.
To really understand how WPF is supposed to work, you should take some time to understand the basics of MVVM. WPF was really designed with MVVM in mind, and so to really get how WPF is supposed to work, you really should take the time to get your head around it. Take a look at:
http://agilewarrior.wordpress.com/2011/01/11/simple-mvvm-walkthrough-part-i/ .
<ListBox ItemsSource="{Binding QueryResults}">
<ListBox.ItemsTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImageSource}"/>
<TextBlock Text="{Binding TextSource}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemsTemplate>
</ListBox>
Will work if you have a list of objects named QueryResults in your code behind. Each object needs to have an string property named ImageSource and a string property named TextSource.
However, since you only need to display a green circle icon for each of the items, you can hardcode the image source. The above will work if you want to have a different icon for each, though.
Also note that in order for this to work, you need to set the DataContext of the window to DataContext="{Binding RelativeSource={RelativeSource Self}}"

Categories