Stretching Toolbar in a toolbartray codebehind and xaml - wpf

I need the following xaml code snippet in cs (code behind file)
<ToolBarTray Width="450" IsLocked="True" >
<ToolBar Width="{Binding ActualWidth,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ToolBarTray}}}">
<Button>B1</Button>
<Button>B2</Button>
</ToolBar>
</ToolBarTray>

If you really intend to use this code in the codebehind than the snippet below should be fine. However, it will not work if you want to create a DataTemplate from code. In that case you need to use FrameworkElementFactory derived types and not FrameoworkElement derived types.
public ToolBarTray CreatetoolBarTray()
{
var tbt = new ToolBarTray
{
Width = 450.0,
IsLocked = true
};
var tb = new ToolBar();
var b = new Binding
{
Path = new PropertyPath("ActualWidth"),
Source = new RelativeSource(RelativeSourceMode.FindAncestor, typeof (ToolBarTray), 1),
};
tb.SetBinding(WidthProperty, b);
tb.Items.Add(new Button() {Content = "b1"});
tb.Items.Add(new Button() {Content = "b2"});
tbt.ToolBars.Add(tb);
return tbt;
}

Related

WPF Binding using a property as value to define a Binding

I have to say I am not getting used to WPF.
I have the problem that I have a Property that contains the value, but I want not to bind to that property.
I want to bind the property to a property named as the value in the property Name.
This is my xaml:
<telerik:RadMultiColumnComboBox x:Name="radMultiColumnComboBox_Part"
Grid.ColumnSpan="3" Grid.Row="1"
SelectedItem="{Binding SelectedItem, ElementName=selector, Mode=TwoWay}"
>
<telerik:RadMultiColumnComboBox.ItemsSourceProvider>
<telerik:GridViewItemsSourceProvider ItemsSource="{Binding ItemsSource, ElementName=selector}"
AutoGenerateColumns="False"
>
<telerik:GridViewItemsSourceProvider.Columns>
<!-- This line below is what I am talking about -->
<telerik:GridViewDataColumn DataMemberBinding="{Binding Name, ElementName=selector}" />
</telerik:GridViewItemsSourceProvider.Columns>
</telerik:GridViewItemsSourceProvider>
</telerik:RadMultiColumnComboBox.ItemsSourceProvider>
</telerik:RadMultiColumnComboBox>
I think mostly a try can explain it best:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var sourceProvider = radMultiColumnComboBox_Part.ItemsSourceProvider as GridViewItemsSourceProvider;
var columns = sourceProvider.Columns;
foreach(var gridViewColumn in sourceProvider.Columns)
{
var binding = new Binding(DisplayMemberPath);
binding.Source = gridViewColumn.DataContext;
// there is no property DataMemberBinding.
BindingOperations.SetBinding(gridViewColumn, Telerik.Windows.Controls.GridViewColumn.DataContextProperty, binding);
}
}
I would prefer a solution in xaml but in c# would also be fine.
Thank you.
This is not possible to do in pure XAML. You'll need to create the binding programmatically. Something like this:
var gridViewColumn = sourceProvider.Columns[0] as GridViewDataColumn;
string propertyToBindTo = selector.Name;
gridViewColumn.DataMemberBinding = new Binding(propertyToBindTo);
The solution:
var provider = this.GridViewItemsSourceProvider;
// var provider = this.radMultiColumnComboBox_Part.ItemsSourceProvider as GridViewItemsSourceProvider;
provider.Columns.Add(new GridViewDataColumn() { DataMemberBinding = new Binding("Name") });

Manually styling separator within context menu to have same style as my XAML version. WPF

I have a context menu open up when I right click on a line I have rendered within WPF. I wanted to increase the leeway allowed to the user for right clicking, so manually checked my lines for their coordinates to check against the right click position, and if we get a hit, open up a context menu that should be exactly the same as if they had right clicked exactly on the line.
The functionality works, however, my two context menus are slightly different. Here's the direct hit context menu:
And the XAML for this context menu:
<Line.ContextMenu>
<ContextMenu>
<Separator>
<Separator.Template>
<ControlTemplate TargetType="Separator">
<StackPanel>
<TextBlock Text="{Binding Name}"></TextBlock>
<Separator/>
</StackPanel>
</ControlTemplate>
</Separator.Template>
</Separator>
<MenuItem Header="Normal" Command="{Binding SetNormalCommand}"></MenuItem>
<MenuItem Header="Reverse" Command="{Binding SetReverseCommand}"></MenuItem>
</ContextMenu>
</Line.ContextMenu>
Here's the context menu when you miss the line but are within the leeway area:
And the manual code I made for this context menu:
if (CheckArea(point.NormalLine, clickX, clickY) || CheckArea(point.ReverseLine, clickX, clickY)) {
MenuItem header = new MenuItem{ Header = point.Name};
MenuItem norm = new MenuItem { Header ="Normal"};
MenuItem reverse = new MenuItem { Header ="Reverse"};
Separator sep = new Separator { };
norm.Command = point.SetNormalCommand;
reverse.Command = point.SetReverseCommand;
contextMenu = new ContextMenu();
contextMenu.Items.Add(header);
contextMenu.Items.Add(sep);
contextMenu.Items.Add(norm);
contextMenu.Items.Add(reverse);
contextMenu.IsOpen = true;
_window.ContextMenu = contextMenu;
break;
}
else {
contextMenu.IsOpen = false;
_window.ContextMenu = null;
}
Is there any code I can add to my manual version that will get me the same style? Thanks in advance.
EDIT: I should mention the 'header' for the context menu on a direct hit is non clickable, whereas the one for the area is clickable, which is another difference.
In your xaml Line.Resources tag you would have your context menu like this:
<ContextMenu x:Key="ContextMenu">
<Separator>
<Separator.Template>
<ControlTemplate TargetType="Separator">
<StackPanel>
<TextBlock Text="{Binding Name}"></TextBlock>
<Separator/>
</StackPanel>
</ControlTemplate>
</Separator.Template>
</Separator>
<MenuItem Header="Normal" Command="{Binding SetNormalCommand}"></MenuItem>
<MenuItem Header="Reverse" Command="{Binding SetReverseCommand}"></MenuItem>
</ContextMenu>
Once you have that you can assign it to a Line like this:
<Line Name="ContextLine" ContextMenu="{DynamicResource ContextMenu}"/>
Then in your code behind, you would do this:
var ctx = this.ContextLine.TryFindResource("ContextMenu") as ContextMenu;
_window.ContextMenu = ctx;
This way you are acquiring the same ContextMenu from Xaml and just appling it through code.
In your C# code you are creating a MenuItem for the Name property but in your XAML code you have defined a DataTemplate for your Seperator where you have binded the Name property. That is why you are getting the difference in the ContextMenu. So in your code behind also you need to define DataTemplate for your Seperator instead of a new MenuItem for the Name property. You can do it as following
if (CheckArea(point.NormalLine, clickX, clickY) || CheckArea(point.ReverseLine, clickX, clickY))
{
MenuItem norm = new MenuItem { Header ="Normal"};
MenuItem reverse = new MenuItem { Header ="Reverse"};
Separator sep = new Separator();
ControlTemplate dt = new ControlTemplate();
FrameworkElementFactory stackPanelElement = new FrameworkElementFactory(typeof(StackPanel));
dt.VisualTree = stackPanelElement;
FrameworkElementFactory txtElement = new FrameworkElementFactory(typeof(Textblock));
txtElement.SetValue(TextBlock.TextProperty, point.Name);
stackPanelElement.AppendChild(txtElement);
FrameworkElementFactory seperatorElement = new FrameworkElementFactory(typeof(Seperator));
stackPanelElement.AppendChild(seperatorElement);
sep.Template = dt;
norm.Command = point.SetNormalCommand;
reverse.Command = point.SetReverseCommand;
contextMenu = new ContextMenu();
contextMenu.Items.Add(sep);
contextMenu.Items.Add(norm);
contextMenu.Items.Add(reverse);
contextMenu.IsOpen = true;
_window.ContextMenu = contextMenu;
break;
}
else
{
contextMenu.IsOpen = false;
_window.ContextMenu = null;
}

WPF Tooltip Image in Datagrid Column

I need to have a datagrid column with some image, and for each cell I need to see, in the tooltip, the bigger image.
The datagrid is binded on a ObsevarbleCollection of MyOBjects, and MyImage and MyBigImage are poperties of MyObject.
With XAML here there is the working code:
<c1:C1DataGrid.Columns>
<c1:DataGridTemplateColumn>
<c1:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding MyImage}" >
<Image.ToolTip>
<ToolTip >
<StackPanel>
<Image Source="{Binding MyBigImage}" />
</StackPanel>
</ToolTip>
</Image.ToolTip>
</Image>
</DataTemplate>
</c1:DataGridTemplateColumn.CellTemplate>
</c1:DataGridTemplateColumn>
</c1:C1DataGrid.Columns>
But I need to move the code in the c# side.
So I've tried this code:
C1.WPF.DataGrid.DataGridTemplateColumn col1 = new
C1.WPF.DataGrid.DataGridTemplateColumn();
FrameworkElementFactory factoryImg = new
FrameworkElementFactory(typeof(Image));
Binding b1 = new Binding("MyImage");
b1.Mode = BindingMode.TwoWay;
factoryImg.SetValue(Image.SourceProperty, b1);
DataTemplate ttTemplate = new DataTemplate(typeof(StackPanel));
FrameworkElementFactory spFactory = new
FrameworkElementFactory(typeof(StackPanel));
spFactory.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);
FrameworkElementFactory imgHolder = new
FrameworkElementFactory(typeof(Image));
Binding b2 = new Binding("MyBigImage");
b2.Mode = BindingMode.TwoWay;
imgHolder.SetBinding(Image.SourceProperty, b2);
spFactory.AppendChild(imgHolder);
ttTemplate.VisualTree = spFactory;
ToolTip tt = new System.Windows.Controls.ToolTip();
tt.ContentTemplate = ttTemplate;
factoryImg.SetValue(TextBlock.ToolTipProperty, tt);
DataTemplate cellTemplate1 = new DataTemplate();
cellTemplate1.VisualTree = factoryImg;
col1.CellTemplate = cellTemplate1;
MyDataGrid.Columns.Add(col1);
cellTemplate1.Seal();
With this code the image in the cell appears correctly, but the tooltip on the image is an empty panel, no image is visualized.
If I create a new BitmapImage the tooltip works, so I think it is a binding problem.
What I'm doing wrong?
Ok solved, I've forgot to set the DataContext property:
imgHolder.SetValue(TextBlock.DataContextProperty, List);

Using code behind only add button to dynamically generated WPF DataGrid column's header

I have a wpf datagrid that has it's columns generated dynamically in code and I need to insert small buttons in each column's header to the right of the text to implement custom (complex) filtering in a popup dialog.
I'm having trouble figuring out how to insert a button into a datagrid column header using only code behind.
This is the route I started going down (commented out bit) but it doesn't work:
private static DataGridTextColumn GetTextColumn(string ColumnName, string FormatString, bool AlignRight)
{
DataGridTextColumn c = new DataGridTextColumn();
c.Header = Test.Common.UIBizObjectCache.LocalizedText.GetLocalizedText(ColumnName);
c.Binding = new System.Windows.Data.Binding(ColumnName);
if (!string.IsNullOrWhiteSpace(FormatString))
c.Binding.StringFormat = FormatString;
if (AlignRight)
{
Style cellRightAlignedStyle = new Style(typeof(DataGridCell));
cellRightAlignedStyle.Setters.Add(new Setter(DataGridCell.HorizontalAlignmentProperty, HorizontalAlignment.Right));
c.CellStyle = cellRightAlignedStyle;
}
//var buttonTemplate = new FrameworkElementFactory(typeof(Button));
//buttonTemplate.Text = "X";
//buttonTemplate.AddHandler(
// Button.ClickEvent,
// new RoutedEventHandler((o, e) => HandleColumnHeaderButtonClick(o, e))
// );
//c.HeaderTemplate=new DataTemplate(){VisualTree = buttonTemplate};
return c;
}
I get an invalidoperationexception "'ContentPresenter' type must implement IAddChild to be used in FrameworkElementFactory AppendChild."
Clearly I'm doing it wrong. :) Any help would be greatly appreciated.
Do you need to use a template? If not use the normal Header property:
string colProperty = "Name";
DataGridTextColumn col = new DataGridTextColumn();
col.Binding = new Binding(colProperty);
var spHeader = new StackPanel() { Orientation = Orientation.Horizontal };
spHeader.Children.Add(new TextBlock(new Run(colProperty)));
var button = new Button();
button.Click += Button_Filter_Click;
button.Content = "Filter";
spHeader.Children.Add(button);
col.Header = spHeader;
dataGrid.Columns.Add(col);
For create a column header with an image button, you can do this in xaml :
<Window.Resources>
<BitmapImage x:Key="Img" UriSource="/Img/yourImage.png"/>
</Window.Resources>
<Datagrid Name="yourDatagrid">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="Btn" Click="Btn_Click" >
<DockPanel>
<Image Source="{StaticResource ResourceKey=Img}" Height="16" Width="16"/>
</DockPanel>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</Datagrid>
Or you can autogenerate this in code behind like this :
Xaml :
<Window.Resources>
<BitmapImage x:Key="Img" UriSource="/Img/yourImage.png"/>
</Window.Resources>
C# :
Datagrid yourDatagrid = new Datagrid();
DataGridTemplateColumn colBtn = new DataGridTemplateColumn();
DataTemplate DttBtn = new DataTemplate();
FrameworkElementFactory btn = new FrameworkElementFactory(typeof(Button));
FrameworkElementFactory panel = new FrameworkElementFactory(typeof(DockPanel));
FrameworkElementFactory img = new FrameworkElementFactory(typeof(Image));
img.SetValue(Image.SourceProperty, (BitmapImage)FindResource("Img"));
img.SetValue(Image.HeightProperty, Convert.ToDouble(16));
img.SetValue(Image.WidthProperty, Convert.ToDouble(16));
panel.AppendChild(img);
btn.AppendChild(panel);
btn.AddHandler(Button.ClickEvent, new RoutedEventHandler(Btn_Click));
DttBtn.VisualTree = btn;
colBtn.CellTemplate = DttBtn;
yourDatagrid.Columns.Add(colBtn);

Listbox carousel - is it possible?

I'm not using a listbox and data binding at the moment, but is it possible to have a listbox work like a carousel and if so, how.
This is what I'm using at the moment, which only works for adding images, not through binding in a listbox... can it still be modified to position each binded canvas+image in the suggested answer?
// add images to the stage
public void addImages()
{
var itemCollection = GalleryModel.DocItemCollection;
foreach (var item in itemCollection)
{
var url = item.ImageUrl;
var image = new Image
{
Source = new BitmapImage(new Uri(url, UriKind.RelativeOrAbsolute))
};
image.Width = 90;
image.Height = 60;
// add the image
LayoutRoot.Children.Add(image);
// Add template here?
// reposition the image
posImage(image, itemCollection.IndexOf(item));
_images.Add(image);
var containingWidth = ActualWidth;
var numberofItemsShown = containingWidth/100;
if (itemCollection.IndexOf(item) < Math.Ceiling(numberofItemsShown)-1)
moveIndex(1);
}
}
// move the index
private void moveIndex(int value)
{
_target += value;
_target = Math.Max(0, _target);
_target = Math.Min(_images.Count - 1, _target);
}
// reposition the image
private void posImage(Image image , int index){
double diffFactor = index - _current;
double left = _xCenter - ((IMAGE_WIDTH + OFFSET_FACTOR) * diffFactor);
double top = _yCenter;
image.SetValue(Canvas.LeftProperty, left);
image.SetValue(Canvas.TopProperty, top);
}
You'd typically use a ListBox for scenarios like this.
The XAML for it would look something like this:
<ListBox x:Name="ImageGalleryListBox">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<tkt:WrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding MyImageItemUri}" Margin="8" Width="100" Height="100" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You can of course template it further to make things look the way you want.
In the code-behind here or in your view model, you'd create class that has an MyImageItemUri property and add instances of it to an ObservableCollection<T>. You can then bind or set the collection to the ItemsSource of the ImageGalleryListBox. You'd create more images dynamically by simply adding more of your image items to the observable collection.

Resources