Listbox in CustomMessageBox always scrolls up - c#

I've got a weird problem with a listbox, which is placed in a custommessagebox.
Problem: You can't scroll down the list, because a "draggesture" up does the same thing than down. So the only thing I got is the respond-animation when your at the top of the list.
I made a little sample:
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
<TextBlock Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Button Background="Bisque" Content="Click Me" Click="Button_Click"></Button>
</Grid>
</Grid>
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var source = new string[200];
for (int i = 0; i < source.Length; i++)
{
source[i] = i.ToString();
}
var dialog = new CustomMessageBox()
{
Content = new ListBox(){ItemsSource = source},
RightButtonContent = "Cancel",
LeftButtonContent = "Store",
VerticalContentAlignment = VerticalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Background = new SolidColorBrush(Colors.Black),
HorizontalContentAlignment = HorizontalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center
};
dialog.Show();
}
}
And here is the link, if you wanna download it:
https://onedrive.live.com/redir?resid=6B27FD720F1FB58D!15988&authkey=!ACG5krcfzsoBxA8&ithint=folder%2csln
Does anyone has an idea whats going wrong?
Thx for any help.

There is a simple fix for that.. Add a Height to the ListBox. Like this.
var dialog = new CustomMessageBox()
{
Content = new ListBox(){ItemsSource = source, Height = 800},
RightButtonContent = "Cancel",
LeftButtonContent = "Store",
VerticalContentAlignment = VerticalAlignment.Top, // Change to Top
VerticalAlignment = VerticalAlignment.Top, // Change to Top
Background = new SolidColorBrush(Colors.Black),
HorizontalContentAlignment = HorizontalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center
};
Change the VerticalContentAlignment and VerticalAlignment to Top so when you set the Height value less than 800 the content is aligned to the top.

just you need to add height for listbox as :
Content = new ListBox(){ItemsSource = source,Height=800}

I do not recommend setting the height, as Daniel Z mentioned you'll lose adaptive design. I managed a better workaround and it worked perfectly for me without setting the height to a static value.
Put the content of the CustomMessageBox in a UserControl. Suppose the xaml content of this control looks like this
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneBackgroundBrush}">
<!-- your listbox and content here -->
</Grid>
in the code behind, implement the loaded event:
public UserControl1()
{
InitializeComponent();
this.Loaded += UserControl1_Loaded;
}
void UserControl1_Loaded(object sender, RoutedEventArgs e)
{
LayoutRoot.Height = (this.Parent as CustomMessageBox).ActualHeight;
}
By setting the height of the grid, your listbox will automatically adjust its height to fit in. Hope it helps.

Related

Can i add elements programatically to XAML? WPF c# [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
I want to add canvas elements by user input. Something like when a button is clicked, a new <Ellipse/> element is added to the XAML file, inside the Canvas.
<Canvas x:Name="GraphDisplayFrame" Grid.Column="1" Grid.Row="0" Grid.ColumnSpan="3" Grid.RowSpan="4">
<Ellipse
Width="50"
Height="50"
Stroke="Black"
StrokeThickness="2"
Canvas.Left="100"
Canvas.Top="100" />
</Canvas>
I'm new to WPF, i'm not sure if this is the right way to do this.
The other thing i'm trying is System.Windows.Media but manipulating the XAMl file looks easier and nicer, since then the locations of the drawings are anchored to the canvas. I'm not sure if i can achieve something similar with System.Windows.Media.
So my question is in the title, but I'm open to other suggestions.
You probably want to learn about Bindings in WPF. Let's say you want your Ellipses be added by user's input (e.g. on Button click) to your Canvas. I'm not sure about Canvas usage for that purpose (it hasn't auto-alignments for child elements), so I used WrapPanel instead (to allow it align items). And we need 2 Buttons (to Add and Remove Ellipses). And I add a Label to display current amount of Ellipses that we have.
XAML:
<Window x:Class="WpfApp2.MainWindow"
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"
xmlns:local="clr-namespace:WpfApp2"
mc:Ignorable="d"
Name ="mainWindow"
Title="Main Window"
Width="800"
MaxWidth="800"
Height="450"
MaxHeight="450">
<Grid x:Name="MainGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*"/>
<ColumnDefinition Width="50*"/>
<ColumnDefinition Width="50*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50*"/>
<RowDefinition Height="50*"/>
<RowDefinition Height="50*"/>
<RowDefinition Height="50*"/>
</Grid.RowDefinitions>
<Label Content="{Binding ElementName=mainWindow, Path=EllipsesCount, UpdateSourceTrigger=PropertyChanged}"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Grid.Row="0"
Background="DimGray"
Foreground="White"
Margin="15,35" />
<Button x:Name="BtnAddEllipse"
Content="ADD ELLIPSE"
Grid.Row="1"
Margin="10, 25" FontSize="22" FontWeight="Bold"
Background="LightGreen"/>
<Button x:Name="BtnRemoveEllipse"
Content="REMOVE ELLIPSE"
Grid.Row="2"
Margin="10, 25" FontSize="22" FontWeight="Bold"
Background="IndianRed"/>
<WrapPanel Orientation="Horizontal"
Background="Gainsboro"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Grid.Column="1"
Grid.Row="0"
Grid.ColumnSpan="3"
Grid.RowSpan="4" >
<ItemsControl ItemsSource="{Binding ElementName=mainWindow, Path=Ellipses, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</WrapPanel>
</Grid>
</Window>
Here you see that Label.Content property is binded to some EllipsesCount property (you'll see it in code-behind below). Also as WrapPanel is binded to Ellipses property.
Code-behind: (for copypaste purpose)
using System;
using System.Linq;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
namespace WpfApp2
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
// Text for Label about Ellipses amount in collection
private object _ellipsesCount = "Current ellipses count: 0";
public object EllipsesCount
{
get => _ellipsesCount;
set
{
_ellipsesCount = "Current ellipses count: " + value;
// When we set new value to this property -
// we call OnPropertyChanged notifier, so Label
// would be "informed" about this change and will get new value
OnPropertyChanged(nameof(EllipsesCount));
}
}
// Collection for Ellipses
private ObservableCollection<Ellipse> _ellipses;
public ObservableCollection<Ellipse> Ellipses
{
get => _ellipses;
set
{
_ellipses = value;
OnPropertyChanged(nameof(Ellipses));
}
}
// Hanlder, which would notify our Controls about property changes, so they will "update" itself with new values
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
// Just for random colors
private readonly Random random = new Random();
public MainWindow()
{
InitializeComponent();
// Initialize collection of Ellipses
Ellipses = new ObservableCollection<Ellipse>();
// Handle when collection is changed to update Label
// with a new amount of Ellipses
Ellipses.CollectionChanged += delegate
{
// Update counter of ellipses when new one added or existing removed
EllipsesCount = Ellipses.Count;
};
BtnAddEllipse.Click += delegate
{
// Create an Ellipse with random stroke color
var ellipse = new Ellipse
{
Width = 50,
Height = 50,
Margin = new Thickness(3),
Stroke = new SolidColorBrush(Color.FromRgb((byte)random.Next(255), (byte)random.Next(255), (byte)random.Next(255))),
StrokeThickness = 3
};
// Add to collection of ellipses
Ellipses.Add(ellipse);
};
BtnRemoveEllipse.Click += delegate
{
// Check, that Ellipses collection isn't null and empty,
// so we can remove something from it
if (Ellipses?.Count > 0)
Ellipses.Remove(Ellipses.Last()); // Removing last element
};
}
}
}
So at result you see, actually, "content of collection of Ellipses", without adding Ellipses directly to window. Binding makes WrapPanel to use collection of Ellipses as source of child elements, that should be in that WrapPanel (instead of original my answer, where we add Ellipse to Canvas as Children).
ORIGINAL answer.
Yes, you can. For example (based on your XAML):
XAML (empty window):
<Window x:Class="WPFApp.MainWindow"
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"
xmlns:local="clr-namespace:WPFApp"
mc:Ignorable="d">
<!-- No even Grid here -->
</Window>
Code-behind (check comments also):
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// Setting Window properties (they not exists in XAML)
// XAML: <Window ... Title="Main Window" Height="450" Width="800">...
this.Title = "Main Window";
this.Height = 450;
this.Width = 800;
// Create main Grid and register some its name
// XAML: ...
var mainGrid = new System.Windows.Controls.Grid();
this.RegisterName("MainGrid", mainGrid);
// Add row and column definitions (as Canvas below needs, at least 4 rows and 3 columns)
for (int i = 0; i < 4; i++)
{
mainGrid.RowDefinitions.Add(new System.Windows.Controls.RowDefinition { Height = new GridLength(50, GridUnitType.Star) });
if (i < 3) // Needn't 4th column
mainGrid.ColumnDefinitions.Add(new System.Windows.Controls.ColumnDefinition { Width = new GridLength(50, GridUnitType.Star) });
}
// Create Canvas and register its name too
// XAML: ...
var canvas = new System.Windows.Controls.Canvas
{
// Just to be able see it at Window
Background = System.Windows.Media.Brushes.LightGray
};
this.RegisterName("GraphDisplayFrame", canvas);
canvas.SetValue(System.Windows.Controls.Grid.ColumnProperty, 1);
canvas.SetValue(System.Windows.Controls.Grid.RowProperty, 0);
canvas.SetValue(System.Windows.Controls.Grid.ColumnSpanProperty, 3);
canvas.SetValue(System.Windows.Controls.Grid.RowSpanProperty, 4);
// Create Ellipse (child canvas element)
// XAML: ...
var ellipse = new System.Windows.Shapes.Ellipse
{
Width = 50,
Height = 50,
Stroke = System.Windows.Media.Brushes.Black,
StrokeThickness = 2
};
ellipse.SetValue(System.Windows.Controls.Canvas.LeftProperty, 100D);
ellipse.SetValue(System.Windows.Controls.Canvas.TopProperty, 100D);
// Add child Ellipse to Canvas
canvas.Children.Add(ellipse);
// or you already can find Canvas by its name:
(this.FindName("GraphDisplayFrame") as System.Windows.Controls.Canvas).Children.Add(ellipse);
// Add Canvas to MainGrid. Find Grid by its registered name too
(this.FindName("MainGrid") as System.Windows.Controls.Grid).Children.Add(canvas);
// Set main Grid as window content
this.Content = mainGrid;
}
}
So, as you can see, XAML markuping is quite more compact, that code-behinded one.

Dynamic UI Layout in DataGridCell using ItemsControl and a Button

This screenshot is from the mockup of my ideal UI. Right now, this is a DataGridTemplateColumn, with header = "ATTENDEES". I am running into issues creating the layout of this DataGridColumn's cell.
I currently have an ItemsControl bound to a List of strings which are the attendees' emails. If there are too many attendees and the ItemsControls' bounds cannot fit in the cell, then a Button with Content = "See more" should appear at the bottom of the cell, under the last attendee email that can be rendered within in the cell's bounds.
Then once the Button ("See more") is clicked, the row should expand to an appropriate height for the attendees to all be visible, and the "See more" Button should disappear.
I could not wrap my head around a clean implementation with a TemplateSelector, ValueConverter, or DataTrigger in pure XAML since I need to compare the ItemsControls' height against the DataGridRow's height and then perform a modification of the cell's layout at runtime by hiding all the items in the ItemsControl that cannot fit within the cell and then showing at Button below it.
I concluded on attempting to do this in the code-behind by subscribing to the ItemControls' load event. I first attempted to use the Height, MaxHeight, DesiredSize.Height, RenderedSize.Height, and ActualSize.Height properties of the ItemsControl but those all were equal to the clipped height of the ItemsControl, not the intrinsic height of all its contents.
I am now measuring the total height of all its items' strings using the FormattedText class. Then I compare this summed height with the row's height and that's as far as I have progressed; I am unsure of how to next change the layout of the cell or if this is even the correct approach.
I feel like I am fighting against the design of the WPF framework by doing rudimentary calculations and crude layout changes to the view in the code-behind.
Any help on this would be greatly appreciated!
Here is my event handler for the ItemsControl.Load:
private void AttendeesItemsControl_Loaded(object sender, RoutedEventArgs e)
{
if (currentRowIndex == -1)
{
return;
}
List<ModelBase> eventsData = ModelManager.events.data;
var eventObj = (Event)eventsData[currentRowIndex];
var attendees = eventObj.attendees;
var totalItemsHeight = 0;
for(int i = 0; i < attendees.Count; i++)
{
totalItemsHeight += heightOfString(attendees[i]);
}
var itemsControl = (ItemsControl)sender;
var controlRenderHeight = itemsControl.RenderSize.Height;
// Check if the intrinsic height is greater than what can be drawn inside the cell
if (controlRenderHeight < totalItemsHeight)
{
var itemHeight = totalItemsHeight / attendees.Count;
var visibleItemsCount = controlRenderHeight / itemHeight;
// .... not sure how to proceed
}
}
And the helper function that measures the height of one of its items:
private int heightOfString(string candidate)
{
var fontFamily = new FontFamily("Lato");
var fontStyle = FontStyles.Normal;
var fontWeight = FontWeights.Normal;
var fontStretch = FontStretches.Normal;
var fontSize = 12;
var typeFace = new Typeface(fontFamily, fontStyle, fontWeight, fontStretch);
var formattedText = new FormattedText(candidate, CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, typeFace, fontSize, Brushes.Black);
return (int)formattedText.Height;
}
Finally, this is the DataGridTemplateColumn's XAML, with the cell template definition:
<DataGridTemplateColumn Header="ATTENDEES" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Path=attendees}" x:Name="AttendeesItemsControl" Loaded="AttendeesItemsControl_Loaded">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock FontFamily="Lato" FontSize="12" FontWeight="Normal" Text="{Binding}">
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
I had to do some real work but I got this set up. Hopefully you can follow it. Here is a screen shot of what it looks like. Obviously i didn't attempt to style it yet. Just getting the resizing. This way you let WPF handle the height of your control you leave it autosized. You just manage your list.
I created a control for the list called AttendeeListControl
<UserControl xmlns:stackoverflow="clr-namespace:stackoverflow" x:Class="stackoverflow.AttendeeListControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Background="GhostWhite">
<Grid.RowDefinitions>
<RowDefinition Height="37"/>
<RowDefinition Height="*"/>
<RowDefinition Height="23"/>
</Grid.RowDefinitions>
<Label Content="Attendees" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top"/>
<ListBox Name="listBoxAttendees" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="1" />
<Button Content="SeeMore" Name="lblMore" HorizontalAlignment="Left" Margin="10,0,0,0" Grid.Row="2" VerticalAlignment="Top" Click="lblMore_Click"/>
</Grid>
</UserControl>
This is the code behind
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Controls;
namespace stackoverflow
{
/// <summary>
/// Interaction logic for AttendeeListControl.xaml
/// </summary>
///
public partial class AttendeeListControl : UserControl
{
public AttendeeListViewModel vm { get; set; }
public AttendeeListControl()
{
InitializeComponent();
var emails = new List<string>() { "email#gmail.com", "email#aol.com", "email.yahoo.com", "email#msn.com" };
var displayed = new ObservableCollection<string>() { emails[0], emails[1] };
vm = new AttendeeListViewModel()
{
EmailList = emails,
DisplayList = displayed,
Expanded = false
};
DataContext = vm;
listBoxAttendees.ItemsSource = vm.DisplayList;
}
private void lblMore_Click(object sender, System.Windows.RoutedEventArgs e)
{
if (vm.Expanded)
{
//remove all but last 2
do
{
vm.DisplayList.RemoveAt(vm.DisplayList.Count - 1);
} while (vm.DisplayList.Count > 2);
lblMore.Content = "Show More";
}
else
{
//don't want the first 2
for (int i = 2; i < vm.EmailList.Count; i++)
{
vm.DisplayList.Add(vm.EmailList[i]);
}
lblMore.Content = "Show Less";
}
vm.Expanded = !vm.Expanded;
}
}
}
and here is the model i used
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace stackoverflow
{
public class AttendeeListViewModel
{
public bool Expanded { get; set; }
public List<string> EmailList { get; set; }
public ObservableCollection<string> DisplayList { get; set; }
}
}
this was all just put on the mainwindow
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:stackoverflow" x:Class="stackoverflow.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:AttendeeListControl HorizontalAlignment="Left" Margin="55,53,0,0" VerticalAlignment="Top"/>
<local:AttendeeListControl HorizontalAlignment="Left" Margin="340,53,0,0" VerticalAlignment="Top"/>
</Grid>
</Window>

WPF "Hidden" behind a Panel

I am encountering an issue in my project and i don't have much more time to do research.
The goal of this project is to allow attending to tests without being physically present.
You receive a file containing your tests, you attend to them (time limited) and send back the file containing your answers.
I got a TextBox in a StackPanel, itself contained in another StackPanel.
All the controls are created programatically.
The controls are added correctly but the TextBox don't react to mouse input.... (in fact only when the textbox is the ast item and even ther only the little last pixel)
UserControl XAML file :
<UserControl x:Class="DataLibrary.View.Questions.ListQuestionInterface"
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:localization ="clr-namespace:DataLibrary.Resources"
xmlns:convert ="clr-namespace:DataLibrary.View.Converters"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Loaded="ListQuestionInterface_OnLoaded">
<UserControl.Resources>
<localization:LocalizedStrings x:Key="LocalizedStrings"/>
<convert:getVisible x:Key="getVisible"/>
<convert:getText x:Key="getText"/>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10*"/>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<StackPanel VerticalAlignment="Stretch" Orientation="Horizontal" x:Name="body" Grid.Row="0" FocusManager.IsFocusScope="True"/>
<Label Grid.Row="0" Margin="0,10,0,0" x:Name="explanations"/>
<Button Content="{Binding Path=type, Converter={StaticResource getText}}"
HorizontalAlignment="Right"
Margin="0,0,10,10"
VerticalAlignment="Bottom"
Grid.Row="1"
Width="120"
Height="20"
Click="DisplayAnswerButton_Click"
Visibility="{Binding Path=type, Converter={StaticResource getVisible}}"/>
</Grid>
</UserControl>
Code Behind:
public partial class ListQuestionInterface : UserControl
{
private UIElement _firstElement;
ListQuestion q;
private bool isTest;
public questionType type
{
get
{
return q.Type;
}
set
{
Console.WriteLine("Attempted to write questionType");
}
}
public ListQuestionInterface(ListQuestion question, bool isTest = true)
{
InitializeComponent();
this.explanations.Content = question.Explanation;
this.DataContext = this;
this.q = question;
this.isTest = isTest;
refreshStackPanel();
}
private void refreshStackPanel()
{
bool first = true;
this.body.Children.Clear();
var enumerators = new Hashtable();
foreach (Question subQuestion in this.q.SubQuestions)
{
enumerators.Add(subQuestion, subQuestion.interfaceEnumerator(isTest).GetEnumerator());
((IEnumerator)enumerators[subQuestion]).MoveNext();
}
//If the Alignemnt property has a value we'll want each pair of control to be aligned wit heach other
//if not, we just want them stacked to the left
if (q.Alignment.HasValue)
{
int maxCount = this.q.SubQuestions.Max(x => x.interfaceEnumerator(isTest).Count());
for (int i = 0; i < maxCount; i++)
{
var stack = new StackPanel
{
VerticalAlignment = VerticalAlignment.Stretch,
HorizontalAlignment = HorizontalAlignment.Center
};
foreach (Question subQuestion in this.q.SubQuestions)
{
try
{
var enumerator = (IEnumerator)enumerators[subQuestion];
var control = enumerator.Current as Control;
((Panel)VisualTreeHelper.GetParent(control)).Children.Remove(control);
control.HorizontalAlignment = q.Alignment.Value;
Canvas canvas = null;
if (control.GetType() == typeof(Button) || control.GetType() == typeof(MaskedTextBox))
{
canvas = new Canvas();
if (control.GetType() == typeof(MaskedTextBox))
{
var thick = control.Margin;
thick.Left -= 5;
control.Margin = thick;
}
if (first)
{
this._firstElement = control;
first = false;
}
control.Focusable = true;
canvas.Children.Add(control);
}
if (canvas == null)
{
stack.Children.Add(control);
}
else
{
stack.Children.Add(canvas);
}
enumerator.MoveNext();
}
catch
{
var blank = new Label
{
Content = "BLANK",
Visibility = Visibility.Hidden
};
stack.Children.Add(blank);
Console.WriteLine("No more items to display");
}
}
this.body.Children.Add(stack);
}
}
else
{
var stack = new StackPanel
{
VerticalAlignment = VerticalAlignment.Stretch,
HorizontalAlignment = HorizontalAlignment.Center,
};
foreach (var subQuestion in q.SubQuestions)
{
var subStack = new StackPanel
{
VerticalAlignment = VerticalAlignment.Stretch,
HorizontalAlignment = HorizontalAlignment.Left,
Orientation = Orientation.Horizontal
};
var enumerator = subQuestion.interfaceEnumerator(isTest).GetEnumerator();
while (enumerator.MoveNext())
{
var control = enumerator.Current as Control;
control.HorizontalAlignment = HorizontalAlignment.Left;
if (control.GetType() == typeof(Button) || control.GetType() == typeof(MaskedTextBox))
{
if (first)
{
this._firstElement = control;
first = false;
}
control.Focusable = true;
}
((Panel)VisualTreeHelper.GetParent(control)).Children.Remove(control);
subStack.Children.Add(control);
}
stack.Children.Add(subStack);
}
this.body.Children.Add(stack);
}
}
private void DisplayAnswerButton_Click(object sender, RoutedEventArgs e)
{
foreach (Question question in q.SubQuestions)
{
question.DisplayAnswers();
}
refreshStackPanel();
}
private void ListQuestionInterface_OnLoaded(object sender, RoutedEventArgs e)
{
this._firstElement.Focus();
}
}
}
I don't think the button have somethng to do with the problem so i'll leave the converters code away.
I've checked some things already :
IsHitTestVisible is never set to false
Defining one of the stackpanels as the FocusScope don't change anything
If i place all my controls into canvas and the canvas into the stackpanel i can freely click on my controls but their placement is completely broken.
The Actual Width/height of the control is sufficient to interact with them(60x20)
The problem seems to have appeared just after using the following trick to set a first focused element on other UserControls (which are not in the current VisualTree anymore)
I really do need help since i can't seem to find someone with a similar problem.
And here are two screenshots to illustrate the problem :
The black arrows shows where my i clicked before taking the screenshot (btw if you know of any software that can do a screenshot WITH the mouse i'm taking it :) )
Ok, my fault here -_-'
I was so tired that i didnt't see that my stackpanel was in fact really BEHIND a Label
In my code i only had 2 row definitions, in the first i put the stackpanel AND a Label (who took the entire space).
And because it was declared later, the label was above the stackpanel thus preventing any mouse interaction with it's content.
Here is the corrected XAML :
<UserControl x:Class="DataLibrary.View.Questions.ListQuestionInterface"
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:localization ="clr-namespace:DataLibrary.Resources"
xmlns:convert ="clr-namespace:DataLibrary.View.Converters"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Loaded="ListQuestionInterface_OnLoaded">
<UserControl.Resources>
<localization:LocalizedStrings x:Key="LocalizedStrings"/>
<convert:getVisible x:Key="getVisible"/>
<convert:getText x:Key="getText"/>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="60*"/>
<RowDefinition Height="20*"/>
<RowDefinition Height="10*"/>
</Grid.RowDefinitions>
<StackPanel VerticalAlignment="Stretch" Orientation="Horizontal" x:Name="body" Grid.Row="0" FocusManager.IsFocusScope="True"/>
<Label Grid.Row="1" Margin="0,10,0,0" x:Name="explanations"/>
<Button Content="{Binding Path=type, Converter={StaticResource getText}}"
HorizontalAlignment="Right"
Margin="0,0,10,10"
VerticalAlignment="Bottom"
Grid.Row="2"
Width="120"
Height="20"
Click="DisplayAnswerButton_Click"
Visibility="{Binding Path=type, Converter={StaticResource getVisible}}"/>
</Grid>
</UserControl>
So in fact, the control was really hidden behind another one -_-'
I now know that this isn't the WPF way of doing things, but i do not know yet how to DataBind to a Template properly
I'm still taking any advices on a good tutorial/starting point for binding to a DataTemplate in XAML, tho only DataBindings i could find were for binding single values to control properties

How to access controls, which are in the ControlTemplate

In my WP8 appliciation's App.xaml file I've defined ControlTemplate as following:
<Application.Resources>
<ControlTemplate x:Name="AddReminderDialog">
<Canvas HorizontalAlignment="Center" Height="320" Width="260"
VerticalAlignment="Center" Background="White" Margin="110,178,110,238">
<TextBlock Foreground="Black" Text="Напомнить" FontSize="15" HorizontalAlignment="Center" Canvas.Left="92" Canvas.Top="38" />
<Button Name="btn1HourBef" BorderThickness="0" Click="NotificationButtonClick" Background="Black" Content="За час" Width="260" FontSize="15" Height="60" Margin="0,70,0,0"/>
<Button Name="btn30MinBef" BorderThickness="0" Click="NotificationButtonClick" Background="Black" Content="За 30 минут" Width="260" FontSize="15" Height="60" Margin="0,130,0,0"/>
<Button Name="btnOnArrDept" BorderThickness="0" Click="NotificationButtonClick" Background="Black" Content="По прилету/вылету" Width="260" FontSize="15" Height="60" Margin="0,190,0,0"/>
<Button Name="btnCancel" BorderThickness="0" Click="NotificationButtonClick" Background="Black" Content="Отменить" Width="260" FontSize="15" Height="60" Margin="0, 250, 0, 0" Visibility="Collapsed"/>
</Canvas>
</ControlTemplate>
</Application.Resources>
I use it as a template of popup as following
private void Image_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
var image = sender as Image; //get the sender iamge
var modelItem = image.DataContext; //get image's data context
const double width = 260;
const double height = 280;
//get the flight id from image's tag property(which was binded in flightInfoDataTemplate)
flightID = Convert.ToString(image.Tag);
//define content for popup
var content = new ContentControl()
{
Width = width,
Height = height,
Background = new SolidColorBrush(Colors.Transparent)
};
//set the template of content to the contentTemplate, which was defined in app.xaml
content.Template = (ControlTemplate)Resources["AddReminderDialog"];
//set popup's datacontext to the image's datacontext
content.DataContext = modelItem;
//popup's child property is setting to our content
popup.Child = content;
popup.Height = height;
popup.Width = width;
popup.VerticalOffset = Application.Current.RootVisual.RenderSize.Height / 2 - height / 2;
popup.HorizontalOffset = Application.Current.RootVisual.RenderSize.Width / 2 - width / 2;
popup.IsOpen = true;
}
I use this popup to set notification. When user taps the image in the first time the btnCancel button should be invisible, because there is nothing to cancel. When the image is tapped the second time, btnCancel should become visible to cancel the notification.
I've set button visibility to collapsed by default. But I have not idea how to access that button in the code behind to make it visible.
So my question is how can I change button's visibility settings in the code behind?
You can do something like this,
private void SearchElement(DependencyObject targeted_control)
{
var count = VisualTreeHelper.GetChildrenCount(targeted_control); // targeted_control is the Canvas
if (count > 0)
{
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(targeted_control, i);
if (child is Button ) // specific button control
{
// do your logic
}
}
}
}

Vertical scrolling in StackPanel without ScrollViewer

I am trying to fix a larger block of code written by previous colleague - it i some sort of report system, output is a table with data. My task was to freeze column headerson top when scrolling. As i am new to this, I made very simple table, to find out how datagrid works:
public MainWindow()
{
this.InitializeComponent();
var dt = new DataTable();
dt.Columns.Add("prvni");
dt.Columns.Add("druhy");
for (int i = 0; i < 100; i++)
{
var row = dt.NewRow();
row[0] = "A" + i;
row[1] = "B" + i;
dt.Rows.Add(row);
}
this.MainGrid.ItemsSource = dt.AsDataView();
}
By lots of searching, I found many topics, which recommended to get rid of ScrollViewer, as the freezed headers are in datagrid by default. This was the original part of code I modified:
var scrollViewer = new ScrollViewer()
{
HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
VerticalScrollBarVisibility = ScrollBarVisibility.Auto
};
scrollViewer.AddHandler(UIElement.MouseWheelEvent, new RoutedEventHandler(this.MouseWheelHandler), true);
var stackPanel = new StackPanel();
scrollViewer.Content = stackPanel;
...
return scrollViewer;
And in another function, it was used/called as:
var reportInfo = ((((sender as DataGrid).Parent as StackPanel).Parent as ScrollViewer).Parent as ReportOutputTabItem).Tag as ReportInfo;
Well - I removed the scrollviewer, and was returning it as StackPanel, however - now I cannot scroll at all. When I searched questions, how to add vertical scrolling to StackPanel, answers were "add ScrollViewer".
So - is there a way, how either make column headers freezed inside the ScrollViewer, or how to enable vertical scrolling in StackPanel without using scrollViewer? (and another possible solution might be to make the vertical size of StackPanel bit shorter, as there are mostly pages of results, but full page is still required to scroll a bit).
XAML part:
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TabControl Name="MainTab" SelectionChanged="MainTabSelectionChanged" ItemTemplate="{StaticResource ClosableTabItemTemplate}"/>
<StackPanel Grid.Row="1" Name="NavigationPanel" Orientation="Horizontal" HorizontalAlignment="Center">
<Button Height="23" Name="FirstButton" Width="40" Content="<<" Click="PageButtonClick" Opacity="0.75"/>
<Button Height="23" Name="PrevButton" Width="40" Click="PageButtonClick" Opacity="0.75" Content="<"/>
<Label Height="23" Name="PageNumberLabel" Width="70" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Content="1/1"/>
<Button Height="23" Name="NextButton" Width="40" Content=">" Click="PageButtonClick" Opacity="0.75"/>
<Button Height="23" Name="LastButton" Width="40" Click="PageButtonClick" Opacity="0.75" Content=">>"/>
</StackPanel>
Thanks in advance.
Well, I finally found solution to this:
Originally, the datagrid was wrapped in the StackPanel, and then in ScrollViewer. I removed the ScrollViewer, and replaces StackPanel with Grid.
Now I have both vertical scrollbars, and frozen column headers.
I removed the entire
var scrollViewer = new ScrollViewer()
{
HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
VerticalScrollBarVisibility = ScrollBarVisibility.Auto
};
scrollViewer.AddHandler(UIElement.MouseWheelEvent, new RoutedEventHandler(this.MouseWheelHandler), true);
var stackPanel = new StackPanel();
scrollViewer.Content = stackPanel;
and replaced with simple var grid = new Grid();
and all stackPanel.Children.Add(dataGrid); replaced with grid.Children.Add(dataGrid);

Categories

Resources