Synchronize generated items from ItemsSource - c#

I'm looking for an easy way to synchronize Text sizes (Button Content), which are generated by an ItemsControl.
I'm using the following Xaml code:
<ItemsControl ItemsSource="{Binding UseCases}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding DisplayName}" Width="200" Height="200">
<Button.ContentTemplate>
<DataTemplate>
<Viewbox>
<ContentPresenter Content="{Binding}" />
</Viewbox>
</DataTemplate>
</Button.ContentTemplate>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I want to make the text on the buttons as big as possible, which already works.
But since the text length is different, the text sizes for each button is also different, which looks odd.
Is there a simple way to tell the Viewbox (or any other way) to take the size of the smallest text and use it for every button?

to limit the size of text just use Padding:
Handling text overflow is the complex part. By some reason (the culprit is - ButtonChrome) the button won't take 'TextBlock.TextTrimmingProperty' attached property, however the AttachedProperty mechanism is designed specifically for the cases like that), leaving you with two options:
Override button's template, lookup for ButtonChrome, get rid of it and replace with something, which has a TextBlock, bind that TextBlock's text to ContentControl.Content.
Manage your text overflow by yourself. Sibscribe for SizeChanged event, get size from the event argument (it mightn't be available anywhere else), get the padding and figure out if text exceeds the available size. Replace the excessive part of it with "..".
The moral - not worth doing.
I'd create uniform quadratic launch buttons and put labels beside them.

Related

XAML SampleData Binding to Values of a List of Strings

OK, so I've searched and searched and searched, and I can't find an answer to this specific angle of my question. I know how to bind to a List, and how to make it auto-updating by making it an ObservableCollection instead of a List. However, with just a list of Strings, how the heck to I bind to the value of each List element?
<DataTemplate x:Key="PageTiles">
<Grid Background="{StaticResource PhoneAccentBrush}"
Margin="6,0,6,12">
<StackPanel VerticalAlignment="Bottom">
<TextBlock Text="{Binding}"
Margin="6,0,0,6" />
</StackPanel>
</Grid>
</DataTemplate>
Here's the LongListSelector that pulls from the list:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" VerticalAlignment="Top" Height="60">
<phone:LongListSelector Margin="0,0,-12,0"
ItemsSource="{Binding PageTitles}"
LayoutMode="Grid"
GridCellSize="150,150"
ItemTemplate="{StaticResource PageTiles}"
SelectionChanged="LongListSelector_SelectionChanged">
</phone:LongListSelector>
</Grid>
Now, I've used a similar layout before to do tiles based off a List of classes that have string properties, but never with a List of strings, and I can't find anything to guide me in the right direction.
So it turns out #har07 was right. The Text="{Binding}" I'd put in the text value was valid, my cell size was just too large for it to show in the 60 high grid that I'd made. Reduced the height of it to less than the stack panel's height and bam, there it was. I didn't even do that on purpose, that's just how I'd left it while I didn't know what to put in there, and because I hadn't recompiled to get the associations fixed, it wasn't finding anything to put in as elements.
So, to recap, to get the values of a List directly instead of something that's part of that value, a simple "{Binding}" does the trick. Lesson learned: double check your size values before running for help XD.

Windows 8 XAML/C# - How to draw dynamic elements on a canvas?

I have canvas control I'm using for a graph and need to be able to dynamically add a variable amount of data points to it. Maybe I'm not aware of a good control to do this type of thing with but I was trying to do it with a GridView as such:
<Canvas x:Name="GraphPointsAndLines" Canvas.ZIndex="2">
<GridView x:Name="gvDataPoints">
<GridView.ItemTemplate>
<DataTemplate>
<Button IsEnabled="False" Canvas.Left="{Binding PointXValue}" Canvas.Top="{Binding PointYValue}" Content="{Binding Value}" Style="{StaticResource ButtonGraphPoint}" />
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</Canvas>
But due to the nature of the gridview it's not recognizing that the child button elements are a part of any such canvas and just ends up stacking them on top of one another. Can anyone offer any suggestions?
This could be interesting: simple technique for databinding to position.
Basically, have an items control with an items source and apply the data template, set its items panel and apply the items container style.

Empty LongListSelector causes infinite ScrollViewer

This is the situation:
I have a datasource that gets filtered by certain attribute (lets call it Checked), into two lists on the viewmodel. Call it New and Old.
New one needs to be displayed into one list, Old one needs to be displayed into the list right under it.
Oh and they need to scroll in unison. So if Old is currently out of screen, it will swim into visibility as the list is swiped up.
I've currently solved this with LongListSelectors like this:
<ScrollViewer VerticalAlignment="Top" VerticalScrollBarVisibility="Auto">
<StackPanel>
<phone:LongListSelector x:Name="NewList" Margin="0,0,0,0" ItemsSource="{Binding New}" SelectionChanged="NewList_SelectionChanged">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<TextBlock Text="{Binding Name}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}" Foreground="{Binding Color}" />
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
<phone:LongListSelector x:Name="OldList" Margin="0,0,0,0" ItemsSource="{Binding Path=Old}" Padding="0,20,0,0">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<TextBlock Text="{Binding Name}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}" FontStyle="Italic" Foreground="{Binding Color}"/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</StackPanel>
</ScrollViewer>
Two longlistselectors inside a stackpanel inside a scrollviewer. Now it all works absolutely fab while there's something in both of those lists.
However, when one of them has no content whatsoever, it immediately expands to fill the entire height of its parent. In this case... the infinite scrollviewer. Which means that if there's nothing in the New list, there will be absolutely nothing visible on the screen whatsoever and if there's nothing on the New list... I can pretty much scroll infinitely after getting past the New list items.
Do I have any options here? Without programmatically creating a ton of Text fields and then trying to attach events to it, or worse, write my own list control? Standard listboxes don't work because they both scroll separately.
Any ideas?
Having two list controls under each other is a genrally a bad idea, because of ScrollViewers inside ScrolViewers.
I would advise you to use a single LongListSelector without any ScrollViewer around it.
Then create a single collection with old an new items and use an ItemTemplateSelector to style them differently.
The problem you are facing is that by the default when emty LLS is measured it's height as you see is 'infinite'. You are using StacPanel which means that second LLS is under infinite LLS.
The simples solution is to set the Height of LLS:
<phone:LongListSelector x:Name="NewList" Height="300" Margin="0,0,0,0" ItemsSource="{Binding New}" SelectionChanged="NewList_SelectionChanged">
If you can - use a Grid with defined rows instead of StacPanel. If you still want to use StackPanel, you can override the method MeasureOverride() in LLS and make extension.
It should work if you do it like this:
namespace Extensions
{
public class LongListSelectorEx : LongListSelector
{
protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize)
{
if (this.ItemsSource == null)
return new System.Windows.Size(this.Width, 0);
if (this.ItemsSource.Count <= 0)
return new System.Windows.Size(this.Width, 0);
return base.MeasureOverride(availableSize);
}
}
}
Watch out also if you haven't got width defined (the return value cannot be NaN - in that situation put 0 instead this.Width). Of course you will also need to check Height of LLS, bacause if you don't your controls can be pushed off the screen, when there are many items in LLS.
You can also read about this here

Get a textblock value in a listbox using GestureServices

Sorry guys, I had asked this question earlier but could not figure out the answer. Made an edit to see if that bumps it, but that did not seem to work. So here is the last try to the question
I can't seem to figure out how one can get the value of a specific textblock in a listbox. To start things off, here is the code:
<ListBox HorizontalAlignment="Left" Name="listItems" VerticalAlignment="Top" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="210" >
<Grid Height="210" Background="#75FFF8DC">
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener Tap="GestureListener_Tap"
DoubleTap="GestureListener_DoubleTap"
Hold="GestureListener_Hold"
Flick="GestureListener_Flick"/>
</toolkit:GestureService.GestureListener>
...CODE...
</></></>...
The code area contains a bunch of other grids, partitions (columns and rows) and textblocks. Here is an example:
<Image Name="XXX" Source="{Binding XXXPath}" Stretch="Fill"
Grid.Column="0"/>
<TextBlock Name="YYY" Grid.Column="1" Grid.Row="0"
Text="{Binding YYYPath}" Foreground="Black"/>
<TextBlock Name="ZZZ" Grid.Column="2" Grid.Row="0"
Text="{Binding ZZZPath}" Foreground="Black"/>
So what I want, is if someone taps the grid (that means anything in the grid, including these textblocks and images), I want to first get the text of the textblock "YYY."
I could have inserted that code into a textblock and used sender as textblock, but I do not want to limit my gestures to one textblock, nor do I want to repeat that for each element in the grid (lots of issues and seems unnecessary).
Edit: If this does not work, I can also implement just one tap gesture (but again, for the whole grid) and use that to get the value of the textblock. Is there no way? Otherwise I will have to do this: Add tap for the textblock and use sender as a textblock, then get the value of the text. But I really do not want to use this approach.
I see you use bindings for your textblocks and image. So why don't you use ( if you haven't already done it) an IList instance of class which hold an information about them? Then set this instance as an ItemSource for your listbox. That way when user taps somewhere on listbox you can catch the SelectedIndex or SelectedItem of a listbox item. And this will help you to figure out which element of IList collection to extract so you could get your text or image or whatever you need.
And you don't need to use GestureServices from external Silverlight Toolkit with Mango. Tap, DoubleTap etc. are built-in.

Populate WPF DataGrid dynamically

My program needs to take input from a fire alarm panel over a serial connection and populate a list based on it. When a new device is reported from the panel, the statement is parsed and the device is added to the device List.
That part of my program all works fine and dandy. The problem now is displaying the list of fire alarm devices to the user.
I am hoping to do this using a DataGrid (unless there's a better way?) but am not able to find a lot of helpful documentation on WPF DataGrids that is relevant to me. Most of what's out there seems to be displaying data from a database. Mine however, needs to update every time the panel spits out a new device description and the device List in my program is appended.
I see I can set AutoGenerateColumns to true and initially display my list just fine. BUT, I would like to customize the column headers. Also this doesn't update when the List is appended so I'm not sure how to "refresh" it.
When AutoGenerateColumns is false, I get no data displayed. When the program runs it shows me the correct number of rows corresponding to the number of items in my list, but no data. Wondering how/if I need to link each column with its corresponding device data member?
Lastly, how do you format a DataGrid to look pretty through re-sizes? I can set column width and all that, but what I want is a few of the columns to be fixed width, and the middle column to expand to fill remaining available area.
This is my first stab at WPF. Any help would be greatly appreciated!
Personaly i dont like DataGrid much. Yes they are easier to bind and they offer built-in resize and sorting options but they are not as flexible as an ItemsControl with a good DataTemplating on your Objects. Let me explain myself.
I tend to populate my ItemsControl with an ObservableCollection. Then, i use a DataTemplate in order to tell my ItemsControl how to display my custom items.
Your CustomObjects can be Modeles objects if your doing MVVM.
If your list is binded to an ObservableCollection, the Added and Removed items will appear dynamicly into your list, which is what i belive your trying to do.
For the column size, you could put a Grid specifying GridColumns width to fixed Width for some columns and * for others so they fill the remaining space.
Here's an alternative to the GridView
I use a ScrollViewer around my ItemControl so if the ItemsControl get too big, you can scroll it.
The ItemsControl's ItemSource is binded to your FireAlarms's ObservableCollection.
The WrapPanel in the ItemsControl will contain each DataTemplate. It's Width is binded to his parent (or ancestor if you will) which is an ItemsControl
<ScrollViewer
Grid.Row="x"
Grid.Column="y"
VerticalScrollBarVisibility="Auto"
Margin="5">
<ItemsControl
BorderBrush="DarkBlue"
BorderThickness="2"
ItemsSource="{Binding Path=FireAlarms}"
ItemTemplate="{StaticResource FireAlarmsTemplate}"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel
Orientation="Horizontal"
Width="{Binding RelativeSource=
{RelativeSource FindAncestor,
AncestorType={x:Type ItemsControl}},
Path=ActualWidth}"
>
</WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
Ok then you need a DataTemplate. You can put the DataTemplate in your windows's ressource or in a DataDictionnary. Lets say you have a class :
FireAlarm
{
Public String AlarmInfo1;
Public String AlarmInfo2;
Public String AlarmInfo3;
}
Here could be a nice DataTemplate to start with :
<DataTemplate x:Key="FireAlarms">
<Border
BorderBrush="SteelBlue"
Background="LightBlue"
BorderThickness="2"
Margin="10"
Padding="10">
<StackPanel
Orientation="Vertical"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="5"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label
Grid.ColumnSpan="3"
Grid.Row="0"
Content="{Binding Path=AlarmName}"
Margin="5,-5,5,10"
FontWeight="Bold"
FontSize="16"
HorizontalContentAlignment="Center"
HorizontalAlignment="Center">
</Label>
<TextBlock
Text="Alarm information 1" Grid.Row="1" Grid.Column="0" />
<TextBox
Text="{Binding Path=AlarmInfo1}"
Grid.Column="2"
Grid.Row="1"
>
</TextBox>
<TextBlock
Text="Alarm information 2" Grid.Row="2" Grid.Column="0" />
<TextBox
Text="{Binding Path=AlarmInfo2}"
Grid.Column="2"
Grid.Row="2"
>
</TextBox>
<TextBlock
Text="Alarm information 3" Grid.Row="3" Grid.Column="0" />
<TextBox
Text="{Binding Path=AlarmInfo3}"
Grid.Column="2"
Grid.Row="3"
>
</TextBox>
</Grid>
</StackPanel>
</Border>
</DataTemplate>
Ok I hope this is usefull for you. My Template will generate 1 square per alarm. If you'd rather have it in a Table like a GridView, you could modify this using a verticaly oriented stack panel and use a grid with variable // invariable column width but since you asked for anything usefull, i tough i'd guive you something fun to work with!
Enjoy!
Some time back I wrote a post Create DataGrid in WPF using code take a look at it, it will help you in creating data grid in dynamic scenarios like yours
If you a dynamic grid (meaning the number and design of the columns are unknown at design time), I do it with code-behind with binding. I generally use the MVVM pattern (if you not familiar with this, I really recommend reading into it since it is THE pattern when working with WPF).
1) You have to set Auto-Generate columns to false of course and give grid a name (here myDataGrid)
GridViewDataColumn newColumn= new GridViewDataColumn();
myDataGrid.Columns.Add(newColumn)
This will add the column to your grid. Now the column will be empty. Now it depends on your data how to fill it with data. If you bind to a known property on the items, do:
newColumn.Binding = new Binding("knownPropertyName");
In most cases though, you do not know the propertyname and bind to an element in the collection.
Then it would like more:
myDoubleCollection.Add(someDoubleValue); //do this for each item in the itemssource of the grid
int index=myDoubleCollection.Count-1;
newColumn.Binding = new Binding(string.Format("myDoubleCollection[{0}]",index));
So this works also.
Another thing to keep in mind is the deletion of columns. This requires some extra work.
That's a lot of questions rolled into one! I suggest you do a bit more background research before asking questions. I would recommend that your read this codeproject article I wrote about the WPF DataGrid a while back:
http://www.codeproject.com/KB/WPF/WPFDataGridExamples.aspx
It will answer most of your questions for you!
Take a look at the MVVM pattern, it'll be a huge help as you create this application.
What you want is an ObservableCollection in the ViewModel. You'll bind the ItemsSource property of the datagrid to this collection. Then have your columns bind to various properties on to display them. Whenever this ObservableCollection has an item appended, your front end should update automatically.
To have a column autosize, set the Width="*".
Here's a sample of a datagrid with MVVM
<DataGrid ItemsSource="{Binding FireAlarmCollection}" SelectedItem="{Binding SelectedFireAlarm, Mode=TwoWay}" AutoGenerateColumns="True" CanUserSortColumns="True" HorizontalScrollBarVisibility="Visible" CanUserResizeColumns="True">
</DataGrid>
As you continue your effort, post separate questions for each issue.

Categories