I am pretty new to WPF and I have tried figuring out how to add a Label appear inside a the following ListView which shows the number of Items currently in the ListView. I've given the ListView padding on the top to make room for the Label.
<ListView x:Name="MyListView" Grid.Row="0" Grid.Column="0" Margin="0,40,0,0" Padding="0" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding DatasetCode}" FontWeight="Bold"/>
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
If anyone can help me out, it would be greatly appreciated.
Edit the Template of ListBox. You can do this by Right-Clicking the ListBox in the Document outline section. And add your Label as below.
...
<ScrollViewer Focusable="false" Padding="{TemplateBinding Padding}">
<StackPanel>
<Label uc:Window2.CountFor="False" />
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</StackPanel>
</ScrollViewer>
...
I have written an attached property CountFor . Code is give below :
#region CountFor attached property
public static bool GetCountFor(DependencyObject obj)
{
return (bool)obj.GetValue(CountForProperty);
}
public static void SetCountFor(DependencyObject obj, bool value)
{
obj.SetValue(CountForProperty, value);
}
// Using a DependencyProperty as the backing store for CountFor. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CountForProperty =
DependencyProperty.RegisterAttached("CountFor", typeof(bool), typeof(Window2), new PropertyMetadata(false, new PropertyChangedCallback(GetCountForChanged)));
private static void GetCountForChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue == false) return;
Label lbl = (Label)d;
lbl.Loaded += (o, args) =>
{
DependencyObject parent = VisualTreeHelper.GetParent(lbl);
while (parent.GetType() != typeof(ListBox))
parent = VisualTreeHelper.GetParent(parent);
ListBox lb = (ListBox)parent;
ICollectionView view = CollectionViewSource.GetDefaultView(lb.ItemsSource);
lbl.Content = "Number of items = " + ((ListCollectionView)view).Count;
view.CollectionChanged += (col, colargs) =>
{
lbl.Content = "Number of items = " + ((ListCollectionView)col).Count;
System.Diagnostics.Debug.WriteLine(((ListCollectionView)col).Count.ToString());
};
};
}
#endregion
Your solution is simple, you could just create an int to count the number of items in your label and then assign a new textblock, you could also completely skip the textblock and simply add the int, check this code:
private void button1_Click(object sender, RoutedEventArgs e)
{
int testcounter;
testcounter = listBox.Items.Count;
TextBlock BlockCounter = new TextBlock();
BlockCounter.Text = testcounter.ToString();
listBox.Items.Add(BlockCounter);
}
Related
I have this XAML
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Left">
<Button
x:Name="BtnTiempo"
Content=""
Style="{StaticResource AppBaseButton}"
Padding="0"
FontSize="17"
Foreground="Red">
<Button.ContextFlyout>
<MenuFlyout x:Name="TiemposMnu">
<MenuFlyout.Items>
</MenuFlyout.Items>
</MenuFlyout>
</Button.ContextFlyout>
</Button>
<TextBlock Text="{Binding Tiempo.StrDescripcion,FallbackValue=?}" Grid.Column="1" TextAlignment="Right" Foreground="Red"/>
</StackPanel>
and this Code that fills TiemposMnu
#region Tiempos
public List<Tiempo> Tiempos
{
get { return (List<Tiempo>)GetValue(TiemposProperty); }
set { SetValue(TiemposProperty, value); }
}
// Using a DependencyProperty as the backing store for Tiempos. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TiemposProperty =
DependencyProperty.Register("Tiempos", typeof(List<Tiempo>), typeof(ItemDetallePedidoControl), new PropertyMetadata(null,new PropertyChangedCallback(OnTiemposChanged)));
private static void OnTiemposChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if(d is ItemDetallePedidoControl p)
{
if (p.Tiempos != null)
{
foreach (var tiempo in p.Tiempos)
{
MenuFlyoutItem item = new MenuFlyoutItem()
{
Text = tiempo.StrDescripcion
};
item.Click += (s, e1) =>
{
p.SeleccionarTiempo(tiempo.IntIdTiempo);
};
p.TiemposMnu.Items.Add(item);
}
}
}
}
#endregion
Everything fires ok. But when I tap / click my button doesn't shows the MenuFlyout.
What I'm doing wrong ?
But when I tap / click my button doesn't shows the MenuFlyout.
If you want to tap/click the button to show MenuFlyout you need to use Button.Flyout . Details please see the remark section.
<Button.Flyout>
<MenuFlyout x:Name="TiemposMnu">
<MenuFlyout.Items>
</MenuFlyout.Items>
</MenuFlyout>
</Button.Flyout>
If you want to trigger the MenuFlyout associate with Button.ContextFlyout, right-click (mouse) or press-and-hold (touch) directly on the button. Mode details please reference official sample.
Here is my scenario. My table has fixed number of columns, say 2, and initially, it has only one visible row, but when the focus is on the last column of row 1 and the user press 'tab', row 2 will be made visible.
My problem is that I can't dynamically select the row I want to make visible because I have to specify its x:Name during compilation.
Below is my current work.
.xaml file
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal" x:Name="SP1">
<TextBox Text="1-1"/>
<TextBox Text="1-2" KeyDown="showNextLine"/>
</StackPanel>
<StackPanel Orientation="Horizontal" x:Name="SP2" Visibility="Collapsed">
<TextBox Text="2-1"/>
<TextBox Text="2-2"/>
</StackPanel>
<!--the remaining rows...-->
</StackPanel>
.cs file
private int lastRowIndex = 1;
private void showNextLine(object sender, KeyRoutedEventArgs e)
{
lastRowIndex++;
string nextLineName = "SP" + lastRowIndex.ToString();
nextLineName.Visibility = Visibility.Visible; // which causes error because nextLineName is string instead of StackPanel
}
Besides, my current implementation is to create 50 rows and make the last 49 invisible initially, and I am open to any method to group all the TextBox more systematically or flexibly.
Thanks for reading.
You could give the parent StackPanel an x:Name or keep a reference to it if you create it dynamically:
<StackPanel x:Name="root" Orientation="Vertical">
<StackPanel Orientation="Horizontal" x:Name="SP1">
<TextBox Text="1-1"/>
<TextBox Text="1-2" KeyDown="showNextLine"/>
</StackPanel>
<StackPanel Orientation="Horizontal" x:Name="SP2" Visibility="Collapsed">
<TextBox Text="2-1"/>
<TextBox Text="2-2"/>
</StackPanel>
<!--the remaining rows...-->
</StackPanel>
...and then get a reference to a child StackPanel using the Children property and some basic LINQ:
private void showNextLine(object sender, KeyRoutedEventArgs e)
{
lastRowIndex++;
string nextLineName = "SP" + lastRowIndex.ToString();
StackPanel child = root.Children.OfType<StackPanel>().FirstOrDefault(x => x.Name == nextLineName);
if (child != null)
child.Visibility = Visibility.Visible;
}
How could I create a StackPanel dynamically?
Like this:
var sp = new StackPanel { Name = "SP3", Orientation = Orientation.Horizontal, Visibility = Visibility.Collapsed };
sp.Children.Add(new TextBlock { Text = "3-1" });
var txt = new TextBlock() { Text = "3-2" };
txt.KeyDown += showNextLine;
sp.Children.Add(txt);
root.Children.Add(sp);
I can't think of an easy way to do the first part of this (since you have 50 stack panels), but if you put all of them in a dictionary, then you could update them using just the key.
Here's the dictionary part done manually:
Dictionary<int, StackPanel> myStackPanels = new Dictionary<int, StackPanel>();
myStackPanels.Add(1, SP1);
myStackPanels.Add(2, SP2);
Then, here's what ShowNextLine would look like:
private void showNextLine(object sender, KeyRoutedEventArgs e)
{
lastRowIndex++;
// Modify the StackPanel whose key is lastRowIndex;
myStackPanels[lastRowIndex] = Visibility.Visible;
}
I need to change the display order of the text in my UWP app but unfortunately I don't find any straight solution to do so.
The textblock in WinRT does not support this property, at least I can't found any information about this feature from MSDN. I found a solution that I need create a "New" textblock control which supports the text display in vertical order but the solution is for silverlight so I'm working on it to see whether it works or not.
This is how textblock works normally:
This is how textblock that I want it to work:
I know there is a way that just setting up the Width and text wraping something but it only works for a certain screen size & resolution, which means under other screen the text will not display properly
Any tips would be appreciated.
To get a "real" vertical text in UWP try the following:
<TextBlock Text="Rotated Text"
FontSize="18"
Foreground="Black">
<TextBlock.RenderTransform>
<RotateTransform Angle="-90" />
</TextBlock.RenderTransform>
</TextBlock>
Edit - UWP verison with user control
VerticalTextBlock - code behind
public partial class VerticalTextBlock : UserControl
{
public VerticalTextBlock()
{
InitializeComponent();
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text",
typeof(string),
typeof(VerticalTextBlock),
new PropertyMetadata(string.Empty, textChangeHandler));
private static void textChangeHandler(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var prop = d as VerticalTextBlock;
var textBlock = prop.TheTextBlock;
var str = (e.NewValue as string);
textBlock.Inlines.Clear();
for (int i = 0; i < str.Length-1; i++)
{
textBlock.Inlines.Add(new Run() { Text = str[i] + Environment.NewLine });
}
textBlock.Inlines.Add(new Run() { Text = str[str.Length-1].ToString()});
}
}
VerticalTextBlock - XAML
<UserControl
...
>
<TextBlock x:Name="TheTextBlock"/>
</UserControl>
Usage and test - XAML
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock x:Name="a" Text="ASD"></TextBlock>
<local:VerticalTextBlock x:Name="b" Text="{Binding ElementName=a, Path=Text}" />
<local:VerticalTextBlock x:Name="c" Text="{Binding ElementName=b, Path=Text}" />
<TextBlock x:Name="d" Text="{Binding ElementName=c, Path=Text}"></TextBlock>
<TextBlock TextAlignment="Center" HorizontalAlignment="Left">
<Run Text="A"/>
<LineBreak/>
<Run Text="S"/>
<LineBreak/>
<Run Text="D"/>
<LineBreak/>
<Run Text="A"/>
<LineBreak/>
<Run Text="S"/>
<LineBreak/>
<Run Text="D"/>
</TextBlock>
</StackPanel>
Original Answer - didn't notice it's UWP not WPF
You got me interested as I've only done this in Android, so there are a few solutions that will work but I decided to try custom control extending TextBlock
public partial class VerticalTextBlock : TextBlock
{
public VerticalTextBlock()
{
InitializeComponent();
}
new public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
new public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text",
typeof(string),
typeof(VerticalTextBlock),
new PropertyMetadata(string.Empty, textChangeHandler));
private static void textChangeHandler(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var prop = d as VerticalTextBlock;
var str = (e.NewValue as string);
var inlines = str.Select(x => new Run(x + Environment.NewLine));
prop.Inlines.Clear();
prop.Inlines.AddRange(inlines);
}
}
Usage in XAML
<local:VerticalTextBlock Text="AABBCCDDEEFF" />
I have two table. Table 1 from a online service which forms the Listbox on my original Page. The second table is located in an internal DB and contains a uniqie field with the same values as a field in table1.
I want to use a listbox in table 1 to load details from table2 in a seperate page.
The code below is my code from my second page however it keeps throwing an exception "Items Collection must be empty before using ItemSource"
Can someone correct this code or sugest better way to do this?
public partial class PlayerProfilePanoramaPage : PhoneApplicationPage
{
object _SelectedPlayer;
string _playerName;
public CollectionViewSource viewsource { get; set; }
public PlayerProfilePanoramaPage()
{
InitializeComponent();
Loaded += new RoutedEventHandler(PhoneApplicationPage_Loaded);
LoadPlayerProfile();
LoadPlayerDetails();
}
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = _SelectedPlayer;
}
private void LoadPlayerProfile()
{
FrameworkElement root2 = Application.Current.RootVisual as FrameworkElement;
var currentplayer = root2.DataContext as Mens_Leaders;
_SelectedPlayer = currentplayer;
_playerName = currentplayer.Name;
}
private void LoadPlayerDetails()
{
ObservableCollection<PlayerProfileTable> playerProfile = new ObservableCollection<PlayerProfileTable>();
viewsource = new CollectionViewSource();
viewsource.Filter += PlayerProfile_Filter;
viewsource.Source = playerProfile;
this.PlayerPanorama.ItemsSource = viewsource.View;
}
void PlayerProfile_Filter(object sender, FilterEventArgs e)
{
if (e.Item != null)
e.Accepted = ((PlayerProfile)e.Item).Name.Equals(_playerName);
}
}
edit: Xaml is currently only simple to text that binding was working. Datacontexts and source bindings not defined in Xaml as defined in code. Xaml code provided below.
<controls:Panorama Name="PlayerPanorama" Title="my application">
<!--Panorama item one-->
<controls:PanoramaItem Name="panoramaPage1" Header="item1">
<Grid Name="Grid1">
<StackPanel>
<TextBlock Height="30" Name="textBlock1" Text="{Binding PlayerName, FallbackValue=Name}" />
<TextBlock Height="30" Name="textBlock2" Text="{Binding Height, FallbackValue=Height}" />
<ScrollViewer Height="387" Name="scrollViewer1" Width="422">
<TextBlock Height="auto" Name="textBlock3" Text="{Binding ProfileBlurb, FallbackValue=Blurb}" />
</ScrollViewer>
</StackPanel>
</Grid>
</controls:PanoramaItem>
I'm using a ListBox to display all values contained in Dictionary<> object:
<ListBox Height="519" x:Name="ContactsListBox" Width="460" Margin="0,0,0,0" SelectionChanged="ContactsListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Key}" Margin="5" Foreground="{StaticResource PhoneAccentBrush}"/>
<TextBlock x:Name ="LastNameData" Text="{Binding Value}" Margin="20, 0" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Content is filled by the following code:
Dictionary<long, Contact> contacts = new Dictionary<long, Contact>();
this.ContactsListBox.ItemsSource = contacts;
Now, I would like to 'know' which specific "Contact" in ListBox is currently selected, either by knowing its Key, or just by extracting value from "LastNameData" TextBlock.
I tried doing something like that, but obviosly it doesn't work:
private void ContactsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListBox lb = this.ContactsListBox.SelectedItem as ListBox;
this.Test_SomeOtherTextBlock.Text = lb.ToString();
}
I would really appreciate your help!
you can even do the follwing:
<ListBox Height="519" x:Name="ContactsListBox" Width="460" Margin="0,0,0,0" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Key}" Margin="5"/>
<TextBlock x:Name ="LastNameData" Text="{Binding Value}" Margin="20, 0" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Grid DataContext="{Binding ElementName=ContactsListBox, Path=SelectedItem}" Margin="0,470,0,0">
<TextBlock Text="{Binding Value}"/>
</Grid>
So you don't need code behind...
BR,
TJ
There are several problems:
In Xaml you probably don't want to display the class name, but a reasonable string, for example:
<TextBlock x:Name ="LastNameData" Text="{Binding Value.LastName}" Margin="20, 0" />
In the selection processing the selected item is KeyValuePair<...>. You could easily find it yourself, if you looked at the returned type in debugger. (Should be kind of a reflex for a programmer, hence a questions like above should never appear :))
private void ContactsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) {
KeyValuePair<long, Contact> kv = (KeyValuePair<long, Contact>)this.ContactsListBox.SelectedItem;
Contact c = (Contact)kv.Value;
Debug.WriteLine(c.LastName);
}
Your code is good, using ListBox.SelectedItem is the right approach.
I think the problem is that you should cast it as ListBoxItem, not ListBox. And also to get to its value using DataContext, so something like along these lines (not tested, I'm not sure about accessing DataContext value in the last line):
private void ContactsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListBoxItem lbi = this.ContactsListBox.SelectedItem as ListBoxItem;
var dataContext = lbi.DataContext;
this.Test_SomeOtherTextBlock.Text = dataContext.Value.ToString();
}
Try this it works for me, will help you to...
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListBox ContactListBox = sender as ListBox;
ListBoxItem listBoxItem = ContactListBox .ItemContainerGenerator.ContainerFromItem(ContactListBox.SelectedItem) as ListBoxItem;
if (listBoxItem == null)
{
return;
}
TextBlock txtBlock = FindVisualChildByName(listBoxItem, "ListTextBlock");
MessageBox.Show(txtBlock.Text);
}
private static T FindVisualChildByName<T>(DependencyObject parent, string name) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
string controlName = child.GetValue(NameProperty) as string;
if (controlName == name)
{
return child as T;
}
T result = FindVisualChildByName<T>(child, name);
if (result != null)
return result;
}
return null;
}
private void ContactsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListBox lb = this.ContactsListBox.SelectedItem as ListBox;
this.Test_SomeOtherTextBlock.Text = lb.ToString();
}
will go something like this..
private void ContactsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListBox lb = this.ContactsListBox.SelectedItem as ListBox;
DataTemplate template=lb.ContentTemplate;
//Now here you have to extract the content of the data template
and then you need to extract the TextBlock from that content.
}
This is just an overview of the functionality.Sorry not able to post complete code.