Dynamically add controls to a silveright StackPanel to Create a menu - silverlight

hello community any one can please say how to add controls dynamically into a Stack Panel
note: what i need is i have to create a menu which get data from database and creates menu items accordingly can any one say how can i create such menus i am new to silver light
I am using silverlight 3 beta and expression blend3 + sketch flow please help me to know how to design those

Excuse the variable names, but here is a code snippet of dynamically adding items to a stack panel
StackPanel split = new StackPanel();
TextBlock expected = new TextBlock();
expected.Text = "Expected Final Bonus";
TextBlock meh = new TextBlock();
meh.Text = Math.Round(((QuoteData)results.First()).ExpectedBonus * 100, 2) + "%";
split.Children.Add(expected);
split.Children.Add(meh);
TextBlock disc = new TextBlock();
disc.Text = "Discretionary Percentage";
TextBlock number = new TextBlock();
number.Text = Math.Round(((QuoteData)results.First()).Discretionary * 100, 2) + "%";
split.Children.Add(disc);
split.Children.Add(number);
Here you can see that I also created the stack panel dynamically, however, you can also create it using XAML.
Something like this should work:
<StackPanel Grid.Row="3" Grid.Column="1" Name="split" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="Auto">

First, if your StackPanel is already in your XAML, handle the Loaded event:
<StackPanel x:Name="spValue" Loaded="spValue_Loaded">
</StackPanel>
private void spValue_Loaded(object sender, RoutedEventArgs e)
{
StackPanel stackPanel = (sender as StackPanel);
stackPanel.Children.Clear();
stackPanel.Children.Add(XamlReader.Load(XElement.Parse(xaml).ToString()) as FrameworkElement);
}
The controls are created with XAMLReader from your stuff loaded from the DB. You can adapt all that to your particular scenario (menu and menu items...)

Related

Auto height for windows control

I have winform hosting WPF control text block where the contents of text block is dynamic. I want to set the height of the text block based on content. mean while text block is hosted in windows usercontrol (TitleBarHost) and in turn this windows control is dynamically added in another winform at run time. Below is piece of code where control is added dynamically.
Control titleBar = new TitleBarHost(titleInfo);
(titleBar as ICustomUI).Initialize(_application, extraData);
panelHeader.Controls.Add(titleBar);
titleBar.Dock = DockStyle.Top;
TitleBarView.xaml
<Grid>
<TextBlock x:Name="txtTitleBar" Text="{Binding Text}" Style="{StaticResource TextBlockStyle}"/>
</Grid>
TitleBarHost.cs
public void Initialize(object application, object configurationData)
{
m_Model = new TitleBarModel(this.titleInfo, application, configurationData);
m_ViewModel = new TitleBarViewModel(m_Model);
m_View = new TitleBarView { DataContext = m_ViewModel };
if (m_ViewModel != null)
{
m_View.FormatString(m_ViewModel.Text);
}
elementHost1.Child = m_View;
}
I need to set the text block height dynamic based on content. I tried to use the ActualHeight prop of text block but doesn't work. Also tried using height Auto, doesn't works!

Stackpanel Visibility - what am I doing wrong . .

I want to make a ListBox containing StackPanels as its elements. The StackPanels will be created and added at runtime, in the C# code behind. .
The StackPanels will contain some images but at the moment none of the image stuff exists yet, so in this code I just wanted to make sure I could do the mechanics.
My XAML looks like this:
<Grid>
<ListBox Name="listBoxImages" BorderBrush="DarkGray" Width="600" Height="300" BorderThickness="3"
Margin="0" Padding="0" Background="#FFC0C0C0"/>
</Grid>
In the C# code-behind I deliberately set a background color of the Listbox different from the one in the XAML to verify I was accessing the ListBox properly in the code-behind.
listBoxImages.Background = Brushes.Blue; //just to show I'm accessing it . . .
That part works; the ListBox displays blue.
Then I went to add a StackPanel. Since there's nothing in it yet I gave it a height and width and a different background color, but I don't see anything. So I checked its visibility and it's false. So I tried setting the visibility using System.Windows.Visibility.Visible but it's still false after that.
StackPanel myStackPanel = new StackPanel();
myStackPanel.HorizontalAlignment = HorizontalAlignment.Left;
myStackPanel.VerticalAlignment = VerticalAlignment.Top;
myStackPanel.Background = Brushes.Bisque; // make something visible
myStackPanel.MinHeight = 50;
myStackPanel.Width = 50;
bool bResult = myStackPanel.IsVisible;
myStackPanel.Visibility = System.Windows.Visibility.Visible;
bResult = myStackPanel.IsVisible;
myStackPanel.Margin = new Thickness(10);
listBoxImages.Items.Add(myStackPanel);
Why is the StackPanel visibility false and is that the reason why I don't see it after adding it to the ListBox? (I'm sorry if this is a noob question)
IsVisible is set to true when it gets rendered on UI.
You can verify by hooking to Loaded event and see value of IsVisible in it by putting breakpoint on the handler.
myStackPanel.Loaded += (s, e) => bResult = myStackPanel.IsVisible;
Also, I verified with your posted code and can see StackPanel rendered on UI.
More verbose definition:
.........
listBoxImages.Items.Add(myStackPanel);
myStackPanel.Loaded += new RoutedEventHandler(myStackPanel_Loaded);
}
void myStackPanel_Loaded(object sender, RoutedEventArgs e)
{
bool isVisible = (sender as StackPanel).IsVisible;
}
Listbox is better populated with an items template. If you want to add arbitrary controls of different types, just use a stack panel.

WPF hit testing a rectangular area

I have a WrapPanel containing an arbitrary number of jagged sized elements. I'd like to implement drag select for my items.
It seems pretty obvious how to HitTest for a point, but how can I find all items within a rectangular area?
You may use VisualTreeHelper.HitTest with a GeometryHitTestParameters argument and a HitTestFilterCallback that checks if a Visual is a direct child of the Panel.
Something like this:
var selectedElements = new List<DependencyObject>();
var rect = new RectangleGeometry(...);
var hitTestParams = new GeometryHitTestParameters(rect);
var resultCallback = new HitTestResultCallback(
result => HitTestResultBehavior.Continue);
var filterCallback = new HitTestFilterCallback(
element =>
{
if (VisualTreeHelper.GetParent(element) == panel)
{
selectedElements.Add(element);
}
return HitTestFilterBehavior.Continue;
});
VisualTreeHelper.HitTest(
panel, filterCallback, resultCallback, hitTestParams);
It looks a little complicated, but the HitTestFilterCallback is necessary to get all Visuals in the visual tree, not only those that actually got hit. For example if your panel contains Label controls, the HitTestResultCallback will only be called for the Border and TextBlock child Visuals of each Label.
The option for controlling hit test visibility is the IsHitTestVisible property. This property allows you to control hit test visibility regardless of the brush with which the UIElement is rendered.
Also, You want to set the Fill to Transperent
<Rectangle Width="200" Height="200" Margin="170,23,12,35" Fill="Transparent" IsHitTestVisible="True" />

WPF Datagrid autogenerated columns

I have bound a datatable to a datagrid in WPF. Now on clicking a row in the grid I need to have a window pop up. But for that, I need to first change a column in the datagrid to be a hyperlink. Any ideas on how to do that?
<DataGrid Name="dgStep3Details" Grid.Column="1" Margin="8,39,7,8" IsReadOnly="True" ItemsSource="{Binding Mode=OneWay, ElementName=step3Window,Path=dsDetails}" />
If I can't change an autogenerated column to hyperlink, is there a way to add a button to each row instead?
Thanks
Nikhil
So, it was really hard to create hyperlink columns to autogenerated datagrid. What I eventually did was this - create buttons to the grid on the fly and then attach a routed event for the same based on the autogenerate event of the datagrid where I shall put my code. I didn't want my code to be hardcoded to the columns and now I'm flexible by changing the datatable on the fly. Here is the code:
private void dgStep3Details_AutoGeneratedColumns(object sender, EventArgs e)
{
DataGrid grid = sender as DataGrid;
if (grid == null)
return;
DataGridTemplateColumn col = new DataGridTemplateColumn();
col.Header = "More Details";
FrameworkElementFactory myButton = new FrameworkElementFactory(typeof(Button), "btnMoreDetails");
myButton.SetValue(Button.ContentProperty, "Details");
myButton.AddHandler(Button.ClickEvent, new RoutedEventHandler(btnMoreDetails_Click));
DataTemplate cellTempl = new DataTemplate();
//myButton.SetValue(Button.CommandParameterProperty, ((System.Data.DataRowView)((dgStep3Details.Items).CurrentItem)).Row.ItemArray[0]);
cellTempl.VisualTree = myButton;
col.CellTemplate = cellTempl;
dgStep3Details.Columns.Add(col);
}
public void btnMoreDetails_Click(object sender, RoutedEventArgs e)
{
//Button scrButton = e.Source as Button;
string currentDetailsKey = ((System.Data.DataRowView)(dgStep3Details.Items[dgStep3Details.SelectedIndex])).Row.ItemArray[0].ToString();
// Pass the details key to the new window
}
I don't think you'll be able to get these advanced UI features out of autogenerated columns. I think you'll either have to decide to program these columns in C# or VB.NET when you retrieve your data and tailor them the way you like, or you'll have to abandon the UI ideas you've mentioned. Autogenerated columns just cannot do that.
However, you could change your approach. Try checking into events like MouseLeftButtonDown, etc. and see if you can simulate the behavior you want by other means.

Programmatically set content in Silverlight DataGrid details

I need to dynamically set the contents within the template of a DataGrid based on information in an external settings file. That settings file specifies which data fields should display in the DataGrid. The administrator of the application can edit the settings to change the fields to display. I cannot hard-code the fields to display.
I can easily add the columns (DataGridTextColumn's) to the DataGrid at runtime. I set a binding to a field in the item source based on the settings, and that displays fine.
Now I need to display details when the user clicks a row. I set up a RowDetailsTemplate with DataTemplate, and added a Grid (or a StackPanel) inside to format the details. If I add to the markup the TextBlocks with bindings to fields, it displays the details just fine.
But how can I set the content of the Grid/StackPanel in the details template programmatically? The Grid/StackPanel controls are null if I try to reference them by name on startup (e.g., in the page Loaded event). I have tried using the Loaded event on the Grid/StackPanel to add the details. That code runs and appears to add the content to the Grid/StackPanel, but nothing actually appears when I click the row. I'm guessing that the problem is that the template/Grid is already loaded and ignores the changes I'm making.
Here's a sample of the code I'm using in the handler for the Loaded event. Even if I do something as simple as this, the details pane doesn't appear when clicking on the row.
<data:DataGrid.RowDetailsTemplate>
<DataTemplate>
<Border Background="LightBlue" >
<StackPanel x:Name="resultsDetailsPanel"
Orientation="Vertical"
Loaded="resultsDetailsPanel_Loaded">
</StackPanel>
</Border>
</DataTemplate>
</data:DataGrid.RowDetailsTemplate>
private void resultsDetailsPanel_Loaded(object sender, RoutedEventArgs e)
{
if (_resultsGridLoaded)
return;
StackPanel detailsPanel = sender as StackPanel;
TextBlock fieldNameTextBlock = new TextBlock();
fieldNameTextBlock.Text = "TESTING";
detailsPanel.Children.Add(fieldNameTextBlock);
_resultsGridLoaded = true;
}
I actually tried your code and is working. Two things you should check:
Is your _resultsGridLoaded variable initialized as false?
Did you set RowDetailsVisibilityMode="VisibleWhenSelected" on your DataGrid?
UPDATE: For some reason is not working anymore. But I did found two ways you can fix it:
Remove the resultsGridLoaded logic.
If you need that logic, you can add a handler for the SelectionChanged event on the DataGrid, in there you can set the _resultsGridLoaded variable to false so the new StackPanel gets its content added correctly:
And the code behind:
private void resultsPanel_Loaded(object sender, RoutedEventArgs e)
{
if (_resultsGridLoaded)
return;
StackPanel pane = (StackPanel)sender;
TextBlock newChild = new TextBlock()
{
Text = "New text"
};
pane.Children.Add(newChild);
_resultsGridLoaded = true;
}
private void grid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_resultsGridLoaded = false;
}
Hope this helps

Resources