C# WPF: Adding custom Usercontrol to Stackpanel - c#

I tried a few things about adding my own created user control element to listbox or stackpanel, but instead of gaining any success, it causes a NullReferenceException and I have no idea why ...
my user control is looking like that:
public partial class ShiftInformationItem : UserControl
{
public ShiftInformationItem()
{
InitializeComponent();
}
}
and the xaml:
<Grid>
<LabelContent="Benutzername:" />
<Label Content="01.03.2014 14:19" />
<TextBox Text="Eintrag ..." />
<Expander Header="Comments (0)">
<Grid Background="#FFE5E5E5"/>
</Expander>
</Grid>
Inside the main window I can add it in a listbox or a stackpanel without any Trouble:
<ListBox>
<controls:ShiftInformationItem />
</ListBox>
or:
<StackPanel Name="ShiftInformationPanel">
<controls:ShiftInformationItem />
</StackPanel>
But when I try to add it with C#:
ShiftInformationList.Items.Add(new ShiftInformationItem());
ShiftInformationPanel.Children.Add(new ShiftInformationItem());
it causes the NullReferenceException and says the object I want to add is null.
Does anybody can explain me why?
Im very thankful for all well meaned and helpful answers in advance!
UPDATE:
public partial class HoBusReceptionMain : Window
{
public HoBusReceptionMain()
{
InitializeComponent();
}
private void RibbonWin_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
RibbonTab r = (RibbonTab)e.AddedItems[0];
switch (r.Header.ToString())
{
case "Shift Information":
InitializeShiftInformationTab();
break;
default:
MessageBox.Show(e.AddedItems[0].ToString());
break;
}
}
private void InitializeShiftInformationTab()
{
//here I want to add the new ShiftInformationItem
}
}
UPDATE 2:
Thanks all comments, it Shows up, that my list || Panel is null ... But both is included in the main window (above HoBusReceptionMain)
I use Ribbons in my application, and when a ribbon tab is selected or loaded the RibbonWin_SelectionChanged Event is fired ... The list or Panel defined below the ribbon definitions

I suspect you are adding that before InitializeComponent() gets called.
Move the code of adding below InitializeComponent() and it will work like it did from XAML. Issue is controls were not initialized before InitializeComponent() gets called and hence resulting in NullReferenceException.
public MainWindow()
{
InitializeComponent();
ShiftInformationList.Items.Add(new ShiftInformationItem());
ShiftInformationPanel.Children.Add(new ShiftInformationItem());
// ShiftInformationPanel is null here
}

Related

WPF UI doesn't update textblock in controltemplate

I made a sample XAML and C# for WPF below:
XAML :
<Button Name="btn">
<Button.Template>
<ControlTemplate>
<Grid>
<TextBlock Name="textblock1" Text="sampletext1" />
<Grid>
<ControlTemplate>
</Button.Template>
</Button>
C# :
Grid grid = btn.Template.LoadContent() as Grid;
var textblock = grid.FindName("textblock1") as TextBlock;
textblock.Text = "sampletext2";
I was able to retrieve and change the textblock text in code behind but it seems that the UI doesn't update it. May I know how to solve this?
Change your code in any of the events
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
TextBlock textBlockInTemplate = (TextBlock)btn.Template.FindName("textblock1", btn);
textBlockInTemplate.Text = "SampleText2";
}
}
Your code makes a copy of the value of the text block then assign a property named "Text" to the copy's value. You never actually change the text inside the button. Your text block is called textblock1, textblock was initialized from textblock1 but once that is done they aren't linked together.
It's been awhile since i've used WPF but you need to re-assign the text value to the button using something like this :
textblock1.Text = textblock;
or even simpler:
textblock1.Text = "sampletext2";

UserControl hidden, Zindex misused?

In my main page I try to show a list of stuff, and on this stuff a userControl as an overlay. In fact I never see it. (In the design view, my userControl is oaky)
XAML MainPage
<Grid>
<Grid x:name="MyPage">
<!-- All this part is visible -->
//Button
//Button
//nice Pic
//Button
</Grid>
<cat:CatPagecontrol x:Name="CatTool" Visibility="Visible" HorizontalAlignment="Center" VerticalAlignment="Center">
<cat:CatPagecontrol.Transitions>
<TransitionCollection>
<PopupThemeTransition/>
</TransitionCollection>
</cat:CatPagecontrol.Transitions>
</cat:CatPagecontrol>
<!-- EDIT I remove the Grid "CatGrid" And the ZIndex -->
</Grid>
I try to switch the ZIndex, no results.
C# File
public MainView()
{
this.CatTool = new CatPagecontrol();
//this.CatTool.Visibility = Visibility.Collapsed;
}
private void showCatSelector()
{
this.CatTool.Visibility = Visibility.Visible;
}
After that I need that one of my buttons show the overlay when clicked.
If you know how to show it, I'm yours. Thanks.
edit : solution find.
Voila !
I've find my problem :
public CatPagecontrol()
{
this.InitializeComponent();
}
I just Initialized in the correct section.

How to avoid RichTextBox grows to right

I have a problem that is driving me crazy, I am working on a project in WPF and I am creating a view.
I was designing a window which contains a "More Options" section, I had even been able to make this section show or hide. This section contained a tabControl which contained a TextBox as bellow:
<TabControl Margin="10,156,12,39" Name="moreTabControl" Grid.Column="1">
<TabItem >
<Grid>
<TextBox Margin="6,6,8,28" Name="myTextBox" />
</Grid>
</TabItem>
</TabControl>
So, in the code behind what I do to show or hide the "More Section" is as following:
public partial class FilterView : System.Windows.Window
{
.....
// Window's height when "more" option are showed
private const int ShowMoreHeight = 386;
/// Window's height when "more" option are hidden
private const int ShowLessHeight = 186;
private bool showMore = false;
private void moreButton_Click(object sender, RoutedEventArgs e)
{
showMore = !showMore;
ResizeWindow();
}
private void ResizeWindow()
{
if (showMore)
{
moreTabControl.Visibility = System.Windows.Visibility.Visible;
moreButton.Content = "<< Less";
MinHeight = ShowMoreHeight;
Height = ShowMoreHeight;
}
else
{
moreTabControl.Visibility = System.Windows.Visibility.Collapsed;
moreButton.Content = "More >>";
MinHeight = ShowLessHeight;
Height = ShowLessHeight;
}
}
......
.....
}
Everything went well until I needed to change the TextBox for a RichTextBox :(, when I run the program and press the "MoreButton" the "more" section is showed as expected but the container window grows a lot to the right!
And I only changed this: <TextBox Margin="6,6,8,28" Name="myTextBox" /> for this: <RichTextBox Margin="6,6,8,28" Name="myRichTextBox" />
Does anyone know what is happening??
Thank you in advance.
I have solved my problem:
Turns out that in my XAML code, my Window had a property called SizeToContent set to "WidthAndHeight", so I changed it to "Manual" and established a value for Width and Height manually.
Hope this helps some else who is experiencing somethig similar.

How to bind a usercontrol property to Listbox

I am having an issue with using a Listbox in my parent project to display a property defined in a user control. To be more specific, I have created a user control which contains a webbrowsercontrol, and I have defined a property called History which contains the history of urls visited during a browsing session. I also have a parent project, which is linked to this user control, in which I am attempting to bind the History property to a Listbox. The point is for someone to be able to see the history urls in a Listbox defined in this parent project, where the history urls are populated by binding to the user control's History property.
Below is my code outlining what I am trying to do:
User Control FullWebBrowser.xaml.cs
public partial class FullWebBrowser : UserControl
{
//ctr
public FullWebBrowser()
{
InitializeComponent();
//for FullWebBrowser.xaml ContentGrid
ContentGrid.DataContext = this;
}
#region Fields
//The navigation urls of the browser.
private readonly Stack<Uri> _NavigatingUrls = new Stack<Uri>();
//The history for the browser
private readonly ObservableCollection<string> _History = new ObservableCollection<string>();
/// <summary>
/// Gets the History property for the browser.
/// </summary>
public ObservableCollection<string> History
{
get { return _History; }
}
The _NavigatingUrls stack is for the forward and back button implementation, which is working fine, and the _History observablecollection contains the urls from the webbrowsing session shown as follows
//If the navigated uri is not in thehistory, add it
if (!_History.Contains(e.Uri.ToString()))
_History.Add(e.Uri.ToString());
These seem to be working properly, as I have implemented the forward and back buttons and they work ok. The issue is that I cannot properly bind the History property defined in the FullWebBrowser.xaml.cs to my parent project which contains a Listbox. This is shown as follows
HistoryPage.xaml
xmlns:my="clr-namespace:FullBrowserControl;assembly=FullBrowserControl">
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<!--Pivot Control-->
<controls:Pivot Title="QUEST">
<!--Pivot item one-->
<controls:PivotItem Header="today">
<Grid/>
</controls:PivotItem>
<!--Pivot item two-->
<controls:PivotItem Header="week">
<Grid/>
</controls:PivotItem>
<!--Pivot item three-->
<controls:PivotItem Header="all">
<StackPanel>
<ScrollViewer>
<ListBox x:Name="ListBox" ItemsSource="{Binding}"
SelectionChanged="ListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=History}" Height="Auto"/>
<TextBlock Foreground="{StaticResource PhoneSubtleBrush}"
Text="{Binding Modified, Converter={StaticResource DateConverter}}"
Margin="24,0,0,12"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Scrollviewer>
</StackPanel>
<!--<Grid/>-->
</controls:PivotItem>
</controls:Pivot>
</Grid>
Note, the dateconverter is ok. Here I am trying to implement a Listbox which shows the url with a timestamp below it.
The code behind for this parent project page is as follows
Historypage.xaml.cs
public partial class HistoryPage : PhoneApplicationPage
{
//Temporary State
public static readonly Setting<int> CurrentHistoryIndex = new Setting<int>("CurrentHistoryIndex", -1);
private FullWebBrowser browser = new FullWebBrowser();
public HistoryPage()
{
InitializeComponent();
//create new instance of FullWebBrowser user control?
this.browser = new FullWebBrowser();
this.DataContext = browser;
//browser.DataContext = this;
//this.DataContext = browser.History;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
//Clear the selection so selecting the same item twice in a row will still raise the SelectedChanged event
CurrentHistoryIndex.Value = -1;
this.ListBox.SelectedIndex = -1;
}
void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ListBox.SelectedIndex >= 0)
{
//navigate to the page containing the user control for the selected item
//how to navigate to Mainpage.xaml and load webbrowsercontrol with selected url??
CurrentHistoryIndex.Value = ListBox.SelectedIndex;
this.NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.Relative));
}
}
}
So this is my basic implementation. No matter what I try I cannot get the Listbox in Historypage to bind to the History property in the FullWebBrowser user control contained outside of the project, I have referenced the FullWebBrowser control using the references option in the solution explorer, in a using declaration at the top of Historypage.xaml.cs, and by an xmlns statement at the top of HistoryPage.xaml
Any assistance with how this may be accomplished would be greatly appreciated! I have been working on this for a couple weeks and cannot find the solution anywhere, even prowling other's posts. I must implement this solution ASAP! thanks for all your help in advance. Please include code to accomplish this, it would help so much to see how this is implemented for future reference!
The DataContext of Your ListBox is FullWebBrowser, therefore your binding should be as follows:
<ListBox x:Name="ListBox" ItemsSource="{Binding Path=History}"/>
To solve this kind of problem yourself try debugging, breakpoint your code then inspect the DataContext properties of the various elements in your UI.

How to Programmatically change a Tab Control's Index & State (Perhaps using commands?)

I have a WPF TabControl which contains a number of TabItems with child UserControls, like this.
XAML:
<TabControl x:Name="tabsMain" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Grid.Row="0" Grid.RowSpan="3" Background="lightgray">
<TabItem Width="100" Header="Profile" FontSize="16">
<InfoControl:InfoTab x:Name="myInfo" />
</TabItem>
<TabItem Width="120" x:Name="Summary" Header="Summary" FontSize="16">
<SummaryControl:SummaryTab/>
</TabItem>
</TabControl>
Within one of the UserControls, lets say, InfoTab, I have a Button. When this Button is clicked I would like to change the index of the TabControl to the SummaryTab and select a radio button on the SummaryTab page.
My problem is that the InfoTab user control does not have access to the MainUserControl which contains the TabControl shown above. I figured out a kludge which changes the SelectedIndex of the TabControl, but this is a very ugly solution and I'd prefer to do something more clean. Also I cannot currently change the RadioButton on my SummaryTab.
My Current C# hack:
Private void btnSummaryDetails_Click(object sender, RoutedEventArgs e)
{
TabControl tabControl = UIHelper.FindChild<TabControl>(Application.Current.MainWindow, "tabsMain");
tabControl.SelectedIndex = 7;
}
Is it possible to use commands or dependency properties to select the SummaryTab and my desired RadioButton? I'm still new to the WPF world, and would love to learn more about this. Thanks in advance.
See my post here for the UIHelper definition I use in the C# above.
One thought comes to mind that will not require too many changes.
First, add an event to your InfoTab class:
public event EventHandler SummaryButtonClicked;
Then handle that in your main form by replacing the control declaration with:
<InfoControl:InfoTab x:Name="myInfo" SummaryButtonClicked="summaryButtonClicked" />
And give a name to your SummaryTab:
<SummaryControl:SummaryTab x:Name="summaryTab" />
Then add the event handler in your main form:
void MainWindow_SummaryButtonClicked(object sender, EventArgs e)
{
this.summaryTab.SelectRadioButton();
}
And add a method in your SummaryTab class to select your radio button.
public void SelectRadioButton()
{
// TODO: something like
myRadioButton.IsChecked = true;
}
You could probably use WPF routed events to solve your problem. Routed events use the WPF visual tree to send events up to parent controls (bubbling) or down to child controls (tunneling) without excessive coupling. I've tried to give a simple example below because I know that routed events can be a bit hairy to learn at first but it's well worth it...
In your main window, define a routed event and add a handler method:
public partial class MainWindow : Window {
public static RoutedEvent ClickedEvent = EventManager.RegisterRoutedEvent(
"Clicked",
RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(MainWindow));
public MainWindow() {
InitializeComponent();
this.AddHandler(MainWindow.ClickedEvent, new RoutedEventHandler(OnClickedEvent));
}
public void OnClickedEvent(object sender, RoutedEventArgs e) {
// do your work here
}
}
In your button click handler, raise the event:
public partial class UserControl1 : UserControl {
public UserControl1() {
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e) {
// raise the event (gets bubbled up to the parent of the control)
this.RaiseEvent(new RoutedEventArgs(MainWindow.ClickedEvent));
}
}
The next step would be to tunnel another event down the visual tree and let the other usercontrol listen for it.
I ended up adding a public method as Jeremy suggested in his post. A simple but effective solution. Thanks Jeremy!
Another key realization was that in order to switch the tabcontrol by index, I can get a reference to the main user control and set the SelectedItem to the TabItem itself, like this:
Private void btnSummaryDetails_Click(object sender, RoutedEventArgs e)
{
//HACK replace this with a command that toggles to a different tab instead of this tab reference
MainUserControl mainUserControl = UIHelper.FindChild<MainUserControl>(Application.Current.MainWindow, "root");
mainUserControl.tabsMain.SelectedItem = mainUserControl.Summary;
mainUserControl.SummaryUserControl.SelectRadioButton();
}
Then as suggested by Jeremy, my solution was something like:
public void SelectRadioButton()
{
// TODO: something like
myRadioButton.IsChecked = true;
}
My XAML structure was like
// my main user control:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
...
x:Name="root"
>
<TabControl x:Name="tabsMain" ...>
<TabItem x:Name="Summary" ... />
</TabControl>
</UserControl>
I think Andrew Jackson's comments are absolutely valid - long term, I plan to investigate using routed command or routed events to traverse the visual tree, but for now I'm sticking with this "quick and dirty" solution as we're not shipping this product. Based on my investigation Routed Events might be a little overkill for this situation.

Categories

Resources