I'm trying to show in screen something like a grades scheme.
The first solution which I did was drawing some rows definitions and columns in a Grid, accumulating some stacks panel (inside there are some rectangles) and setting in the row. But I don't like that idea.
I was using several bucles and I'd like to show that in a document viewer. Althoug I can imagine I may create a scheme o something easier.
UPDATE 1:
At this moment, I've got a class which contains as properties:
SubjectName: string
IsCorrect: bool
And others properties but they don't matters right now. Then I have all in a List. I don't know if it's useful to user a LINQ function to group by SubjectName and get its average score:
Classify elements from a list to another classification with average
But I really have interest in the control.
Personally I would (assuming the data is in a database of some kind) host those rows in an ItemsControl of some kind. The ItemsControl would be bound to a collection of the Items.
If I had a little leeway, I would make it a plain old list box and customize the styling of the items). The bar chart portion at the end should be fairly easy to accomplish by having a rectangle whose width is bound to the grade property or whatever which is applied as a percentage of the width of the container it is in.
So I would have a Subject Class (which is created from the data source) with several properties :
Name (e.g. "Times Tables") Represented by a TextBlock
Grade (e.g. 52% ) Represented by a rectangle bound to a property which multiplies that value by the width of the grid in which the rectangle resides
Action (e.g. "Repeat") represented by another textblock.
I will post an example in a little while.
Example
OK so The window has a very simple layout, a Grid with a listbox in it. The listbox is bound to a design time datasource that I set up in Expression Blend.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TreeViewMFagic"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
x:Class="MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Grid x:Name="LayoutRoot" MouseRightButtonDown="ShowContext" DataContext="{Binding Source={StaticResource dsSubjects}}">
<ListBox ItemTemplate="{DynamicResource ItemTemplate}" ItemsSource="{Binding Collection}" HorizontalContentAlignment="Stretch"/>
</Grid>
</Window>
The ItemTemplate I put in the App.Xaml resource Library as I like keeping my Window and UserControl xaml files nice and clean and using resource dictionaries as much as possible to keep stuff out of the way. Anyhow, below is the ItemTemplate.
<DataTemplate x:Key="ItemTemplate">
<Grid Height="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.029*"/>
<ColumnDefinition Width="0.67*"/>
<ColumnDefinition Width="0.168*"/>
<ColumnDefinition Width="0.133*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding SubjectName}" VerticalAlignment="Bottom" d:LayoutOverrides="Width" Grid.ColumnSpan="1" Margin="0" Grid.Column="1"/>
<TextBlock Text="{Binding Action}" VerticalAlignment="Top" d:LayoutOverrides="Width, GridBox" Grid.ColumnSpan="1" Grid.Column="3" Margin="0"/>
<Border Grid.ColumnSpan="1" Grid.Column="2" Margin="0" d:LayoutOverrides="Height" Background="#A3000000" CornerRadius="5" Width="{Binding PercentCorrect}" HorizontalAlignment="Left" >
<TextBlock Text="{Binding PercentCorrect}" HorizontalAlignment="Center"/>
</Border>
<TextBlock TextWrapping="Wrap" Text="{Binding Number}" d:LayoutOverrides="Width, Height"/>
</Grid>
</DataTemplate>
And the finished product looks like this:
Now I cheated a little here. I bound the width of the border element for the grade directly to the percent. Given more time to mess with this, I probably would have made a ViewModel and had values for both the shaded part and the unshaded part which added up to 100%. Then bound column widths of a grid that I would put the border in to those values to have true percentage. Regardless, there is a starting point.
Related
Each UserControl I have in a stackPanel has a WrapPanel, and each UserControl in this WrapPanel is a word (so they have a different size from each other, depending on the length of the word), so that a sentence appears.
Here is an image to help you understand better:
In pink it is the UserControl "sentence" that contain each of them ONE wrapPanel
In Green it is all the UserControl "word" that have all different size according to the word length.
Here is the UserControl "word":
<UserControl x:Class="Coran_seul.UC.UserControl_WordInVerse"
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:local="clr-namespace:Coran_seul.UC"
mc:Ignorable="d" Margin="5,0,5,0">
<StackPanel>
<TextBlock x:Name="textBlock_arabic" Text="ٱلْأَنفَالُ " FontSize="20" HorizontalAlignment="Center" FontFamily="Noto Naskh Arabic" MouseEnter="textBlock_arabic_MouseEnter" MouseLeave="textBlock_arabic_MouseLeave" Cursor="Hand" MouseDown="textBlock_arabic_MouseDown"/>
<TextBlock x:Name="textBlock_french" Text="Les surplus (de bénéfice)" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</StackPanel>
And here is the UserControl "sentence" (with the wrapPanel I have a problem with)
<UserControl x:Name="userControl" x:Class="Coran_seul.UC.UserControl_VerseInSurah"
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:local="clr-namespace:Coran_seul.UC"
mc:Ignorable="d" Margin="0,5,0,5"
>
<Border x:Name="Border_Verse" >
<StackPanel Orientation="Horizontal" x:Name="grid">
<Label x:Name="Label_VersetNum" Content="2" Height="{Binding ActualHeight, ElementName=WrapPanel_Mots, Mode=OneWay}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" VerticalAlignment="Stretch" HorizontalAlignment="Left" MouseDown="Label_VersetNum_MouseDown"/>
<WrapPanel x:Name="WrapPanel_Mots" Width="{Binding ActualWidth, ElementName=userControl, Mode=OneWay}" />
</StackPanel>
</Border>
The problem :
Now that I've entered the code, here's the problem I'm having with my wrapPanel:
The last word of each line does not wrap, even if it is larger than the space left (= it is therefore hidden/cut off at the edge of the wrapPanel)
Again I have a video to better explain the problem :
https://streamable.com/lpdf38
I don't know what it's due to and I'm desperately looking since yesterday not to have this problem anymore, knowing that the stackPanel containing all the userControls has a right margin of 20 so that it's not hidden behind the scrollbar.
Please help me to find a solution so that the word as long as it is hidden even by one pixel is wrapped to the next line.
Thank you,
The problem is that you bind the WrapPanel's Width to that of its UserControl parent without subtracting the Width of the Label element.
You should not need to bind any Width at all, when you use suitable Panel elements, like e.g. a Grid or a DockPanel instead of StackPanel.
<Border x:Name="Border_Verse">
<DockPanel>
<Label x:Name="Label_VersetNum" DockPanel.Dock="Left" .../>
<WrapPanel x:Name="WrapPanel_Mots">
</DockPanel>
</Border>
<Border x:Name="Border_Verse">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label x:Name="Label_VersetNum" Grid.Column="0" .../>
<WrapPanel x:Name="WrapPanel_Mots" Grid.Column="1">
</DockPanel>
</Border>
You may also consider to use an ItemsControl to display a sentence. It would use a WrapPanel as its ItemsPanel and the word UserControl in its ItemTemplate:
<ItemsControl ItemsSource="{Binding Words}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<uc:UserControl_VerseInSurah/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
where Words is a collection of word data item objects in the view model, i.e. the object in the DataContext of the view. The two TextBlocks in the word UserControl would have their Text property bound to an appropriate property on the word data item class.
You may not even need the word UserControl at all, since you can simply move the elements from its XAML into the DataTemplate that is used as ItemTemplate.
See Data Templating Overview for details.
For reference, this is a chat application. This should give you some idea of a final goal.
Additionally, I am very new to WPF. This is one of my first applications and I am making this as a proof of concept. I've been using Windows Forms up until this point, so any comparison or reference to it would help me understand a bit better.
So, the issue at hand:
The chat box for my chat application is a StackPanel (should it be?) which is programmatically populated with TextBlock elements. I need to find a way to scroll down this StackPanel once the available space runs out. I also need it to automatically scroll to the bottom (like a chat would; you wouldn't be able to see the most recent message otherwise).
The question: How can I make a ScrollViewer properly size dynamically with a StackPanel?
Additionally, I also need this StackPanel to size dynamically as the window is sized. This, in turn, would affect the scroll bar.
My current "solution" is to use a ScrollViewer with the StackPanel nested. However, the ScrollViewer and StackPanel do not size properly with a change in window size, as shown in screenshot #2. The XAML code and a screenshot of the designer is shown below.
<Window x:Name="Main" x:Class="dprCxUiDemoWpf.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:dprCxUiDemoWpf"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid Background="#FF171717">
<TextBox x:Name="ChatBox" TextWrapping="Wrap" Background="#FF4F4F4F" Foreground="White" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" RenderTransformOrigin="-0.118,12.093" Margin="146,0,0,1" VerticalAlignment="Bottom" Height="46" BorderBrush="#FFFF00F3" KeyDown="ChatBox_KeyDown"/>
<Image x:Name="DprLogo" Source="/dprCxUiDemoWpf;component/Images/logo1.png" HorizontalAlignment="Left" Height="60" Margin="10,0,0,10" VerticalAlignment="Bottom" Width="123"/>
<ScrollViewer Background="Red" Height="Auto" Width="Auto" ScrollViewer.CanContentScroll="True" Margin="146,0,0,0" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" VerticalAlignment="Top" MinHeight="372">
<StackPanel x:Name="ChatPanel" Height="Auto" Width="Auto" Background="DimGray" ScrollViewer.CanContentScroll="True" />
</ScrollViewer>
</Grid>
</Window>
(source: gcurtiss.dev)
Please note the following regarding the first screenshot:
A. The black column (containing the logo) is simply the background color of the window; there is nothing there.
B. The gray portion is ChatBox (the StackPanel)
C. The pink highlighted box below is the text box where messages are entered.
I appreciate and accept any and all help!
You have to use the Grid panel properly. You layout its children by defining rows and columns. Grid is a column/row based layout container. Then configure row and column definitions to control the resize behavior of the cells and their content.
Using absolute positioning and sizes will of course prevent controls from responding to their parent's size changes. Most control stretch to fit the available space. But this requires dimension properties being set to Auto.
You said you are "more of a hands-on learner", but you should still read some documentations. Otherwise you will significantly slow down your progress until stagnation.
There are tons of blogs waiting for you to read them. To poke around in the dark will get you nowhere. You need at least to know the basics. Instead of waiting 13+ hours for a copy & paste ready answer, you could have finished multiple tutorials already and solve this on your own. Success is a good feeling. This is a very trivial problem.
Find yourself a good tutorial that you find easy to understand and start to experiment with the Grid after reading it.
According to your posted code, you obviously have zero idea how this panel works and how you can use it to align your controls.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2">
<StackPanel />
</ScrollViewer>
<Image Grid.Row="1" Grid.Column="0"
Height="60"
Width="123 />
<TextBox Grid.Row="1" Grid.Column="1" />
</Grid>
I need to find a way to scroll down this StackPanel once the available space runs out. I also need it to automatically scroll to the bottom (like a chat would; you wouldn't be able to see the most recent message otherwise)
You should read about data-binding and MVVM first. Usually you hold an ObservableCollection of items on your VM and bind them to eg a ListBox on your View.
Then you can scroll-down the ListBox, each time a new item got added to your collection:
private void ScrollRecordsListToBottom()
{
if (RecordsList.Items.Count == 0)
return;
var lastItem = RecordsList.Items[RecordsList.Items.Count - 1];
RecordsList.ScrollIntoView(lastItem);
}
I need to create a simple user control which is used for displaying a descriptive message to the user. The XAML definition that I have for this control is as follows:
<UserControl x:Class="Console.WPF.DisplayMessageView"
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:DesignWidth="550"
HorizontalAlignment="Stretch">
<UserControl.Resources>
<!--Converters-->
<BooleanToVisibilityConverter x:Key="BooleanToVisibility" />
</UserControl.Resources>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="10,5,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox
Grid.Column="0"
VerticalAlignment="Top"
FontWeight="Normal"
FontSize="10"
Padding="12,4,2,2"
Height="74"
Background="White"
BorderThickness="1"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Left"
TextWrapping="Wrap"
Text="{Binding DetailsText, Mode=OneWay}" />
<Image Source="..."
Stretch="None"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="0,2,0,0"
Visibility="{Binding IsErrorMessage, Converter={StaticResource BooleanToVisibility}}"/>
</Grid>
</UserControl>
This UserControl is currently embedded within a sizable stack of containers (DockPanels, StackPanels, etc.) and user controls. However, it's first parent container is a DockPanel. It is the last control in the DockPanel and the LastChildFill property is set to true on the panel.
My problem is that when I use this user control, regardless of HorizontalAlignment settings, it alway sizes the user control to the size of the text field. This means that if the text field is short, the user control will be small and look odd since it's not filling it's respective content hole. If the text is a vary long block of descriptive text, it doesn't wrap and continues to expand and is hidden off the edge of the screen.
I can't figure out how to fix this. I also want to note that this application does not use scroll bars, which have been band from use, except on list boxes. I can't use scroll regions for this application, however, this message object absolutely must expand to fill the container but NOT expand any larger.
How can I accomplish this without constraining a fixed Width value on my user control instance? Instead, I need the user control to grow with the container, when the window grows as well.
NOTE
I'm adding the C# tag, just in case this has to involve a code-behind change. I really hope it's just a matter of setting the right set of magic-properties, which have eluded me, however, I'm not opposed to encoding a rock-solid code behind solution, so long as it specifically does what I need it to do.
I have the following piece of XAML code in my WPF application,
<StackPanel DockPanel.Dock="Top" >
<TextBlock Style="{StaticResource HeaderTextBlock}">Import Log</TextBlock>
<ScrollViewer Height="400" VerticalScrollBarVisibility="Auto">
<TextBlock Name="ImportFeedBack"></TextBlock>
</ScrollViewer>
</StackPanel>
which dispalys the ImportFeedBack string (in case someone is wondering, I'm using Caliburn.Micro as MVVM framework, so that the content of the TextBlock is bound by naming convention to a property of same name in my ViewModel).
The value can vary heavily in length. I want it to use the whole available space (but it should not resize the application!), and only if that is not enough, add a vertical scroll bar.
If I delete the Height="400" in the ScrollViewer, it resizes the app for big strings, and if I leave it there, it (obviously) just uses 400 height, but ads he scroll bar when needed.
How can I get it to use all the available space, and only if that is not enough, to creata a vertical scroll bar?
Instead of StackPanel use different panel like Grid or DockPanel. In the example below second row will take all available space not allocated by first row and not expand beyond that which is when scroll bar should appear when text is longer.
<Grid DockPanel.Dock="Top" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Style="{StaticResource HeaderTextBlock}">Import Log</TextBlock>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
<TextBlock Name="ImportFeedBack"></TextBlock>
</ScrollViewer>
</Grid>
I have a simple design with several hubsections into which I want to display a ListBox inside a Grid. The ListBox itself will contain two elements : a TextBlock and a TextBox.
The TextBlock in each HubSection will display the same property, so the data binding is quite easy. However, the TextBox should display a different property in each separate HubSection. I am lost and do not even have a clue on how I should proceed.
Here is my XAML code :
<Page.Resources>
<ResourceDictionary>
<DataTemplate x:Key="BaseGridTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="12"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock
Text="{Binding PlayerName}"
FontSize="28"/>
<TextBox Text="{Binding OtherProperty, Mode=TwoWay}"
Background="Lavender" FontSize="28"
Grid.Column="2">
</TextBox>
</Grid>
</DataTemplate>
</ResourceDictionary>
</Page.Resources>
Then in my HubSections, I want to use the above template to display data, like such:
<HubSection Width="350" x:Uid="Test" Header="Test">
<DataTemplate>
<ListBox x:Name="TestListBox" Grid.Column="1"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource BaseGridTemplate}"
>
</ListBox>
</DataTemplate>
</HubSection>
In my UI, both properties (PlayerName and OtherProperty) are displayed correctly. But what I would like to do is to bind to a different property for the TextBox (i.e. different from OtherProperty).
I really have no clue on how I should proceed, or if it even is possible.
I thought I could to something like defining my TextBox in the Resources section like such :
<TextBox Text="{Binding, Mode=TwoWay}">
And then Hope I could add something in the HubSection part ?
Assuming you have a way of knowing when you have to use your different properties I think the best way for you to do this is to use a template selector and have different kind of templates based on what you want to display.
Another alternative would be to use a value converter for TextBox and use the binding you suggested:
<TextBox Text="{Binding, Mode=TwoWay, Converter={StaticResource YourConverter}">
These are just some ideas to get you started, another important thing to know is if are you trying to use a different property on the same object?