Use a UserControl inside a Ribbon - wpf

I want to use a UserControl inside a RibbonControl.
For example:
<RibbonWindow
xmlns:uc="clr-namespace:UserControl1;assembly=UserControl1">
<Grid ShowGridLines="False" Margin="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="100*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100*"/>
</Grid.ColumnDefinitions>
<Ribbon Grid.Row="0">
<RibbonApplicationMenu >
<RibbonApplicationMenuItem x:Name="miExit" ImageSource="Images/large_exit.png" Header="Exit" />
</RibbonApplicationMenu>
<RibbonTab Header="Test">
<uc:RibbonGroups/>
</RibbonTab>
</Ribbon>
<uc:Content Grid.Row="1" />
</Grid>
</RibbonWindow>
You see two UserControls, uc:RibbonGroups and uc:Content. While the second one (uc:Content) works fine, the UserControl inside the RibbonControl won't work.
What i missed?
I also try this approach:
How to set the usercontrol for ribbon window in WPF?
an set a grid inside the RibbonTab and then the UserControl;
<Ribbon Grid.Row="0">
...
<Grid>
<RibbonTab Header="Test">
<uc:RibbonGroups/>
</RibbonTab>
</Grid>
</Ribbon>
instead of
<Ribbon Grid.Row="0">
...
<RibbonTab Header="Test">
<uc:RibbonGroups/>
</RibbonTab>
</Ribbon>
Edit:
UserControl now only holds the content of RibbonTab, RibbonTab itself moved to the main window
<UserControl ...>
<StackPanel Orientation="Horizontal">
<RibbonGroup Header="Test" >
<RibbonButton SmallImageSource="Images/small_save.png"
LargeImageSource="Images/large_save.png" />
<Image Source="Images/large_expert_enabled.png"></Image>
</Stackpanel>
</UserControl>
RibbonGroup are shown with the Header but without the images, and the separate Image are shown itself. So maybe there are now any suggestions what goes wrong?
EDIT2:
If i step into with Snoop, the RibbonButtons and their corresponding images are present.
EDIT3:
I tried another way with resources, but same problem as above..
How it looks, when i have the RibbonTab in the resource/UserControl:
http://s12.postimg.org/lpposl48t/DLL_Ribbon_Tab_fail_part.png
How it looks, when i have the RibbonGroup in the resource/UserControl:
http://s12.postimg.org/oiiwcm4l9/DLL_Ribbon_Group_fail_part.png

After a lot of research i found this from Ben Barefield:
http://www.benbarefield.com/blog/2011/03/04/ribbon-tab-definition-on-usercontrol/
He use a trick and move the ribbontab into a resourcedictionary.
Works well, but i also wanted to add some click events to this ribbontab (with view specific actions....) so i decided to fall back to the usercontrol.
The trick is, to detach the ribbontab from the usercontrol before adding it to the other control.
In detail:
add this methods to the usercontrol codebehind:
private void AttachTabToRibbon(RibbonTab tab)
{
Ribbon Ribbon = null;
Ribbon = getRibbon();
if (Ribbon == null)
return;
Ribbon.Items.Add(tab);
}
private Ribbon getRibbon()
{
var control = VisualTreeHelper.GetParent(this);
Ribbon Ribbon = null;
while (control != null && Ribbon == null)
{
var numberOfChildren = VisualTreeHelper.GetChildrenCount(control);
for (int i = 0; i < numberOfChildren; ++i)
{
var child = VisualTreeHelper.GetChild(control, i);
if (child is Ribbon)
{
Ribbon = child as Ribbon;
return Ribbon;
}
}
control = VisualTreeHelper.GetParent(control);
}
return null;
}
private void userControl_Loaded(object sender, RoutedEventArgs e)
{
Ribbon ribbonMenu = (Ribbon)this.FindName("ribbon");
RibbonTab ribbonTabAdminInterface = (RibbonTab)this.FindName("ribbonTab");
ribbonMenu.Items.Remove(ribbonTab);
ucStackPanel.Children.Remove(ribbonMenu);
AttachTabToRibbon(ribbonTab);
}
furthermore call the userControl_Loaded:
<UserControl ...
Loaded="userControl_Loaded"
>
<StackPanel x:Name="ucStackPanel">
<Ribbon x:Name="ribbon" >
<Ribbon.ApplicationMenu>
<RibbonApplicationMenu
</RibbonApplicationMenu>
</Ribbon.ApplicationMenu>
<RibbonTab x:Name="ribbonTab"/>
...
</Ribbon>
...
</StackPanel>

Related

Caliburn.Micro : How to bind a specific Item of Conductor.Collection.AllActive to a ContentControl

My goal is to have 4 different active ViewModels displayed in a grid on the ShellView. The issue is that I have not been able to figure out how to wire up a ContentControl to a specific Item in Items of the Conductor. How can his be done?
Here is a simplified version of what I and trying to do.
SolutionExplorer
ShellViewModel:
namespace ContentControlTest.ViewModels
{
public class ShellViewModel : Conductor<object>.Collection.AllActive
{
public ShellViewModel()
{
ActivateItem(new UC1ViewModel());
ActivateItem(new UC2ViewModel());
ActivateItem(new UC3ViewModel());
ActivateItem(new UC4ViewModel());
}
}
}
ShellView:
<Window x:Class="ContentControlTest.Views.ShellView"
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:ContentControlTest.Views"
xmlns:cal="http://www.caliburnproject.org"
mc:Ignorable="d"
Title="ShellView" Height="450" Width="800"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Row="0" Grid.Column="0" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<ContentControl cal:View.Model="{Binding UC1ViewModel}" cal:View.Context="{Binding Items[0]}"/>
</ScrollViewer>
<ScrollViewer Grid.Row="0" Grid.Column="1" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<ContentControl cal:View.Model="{Binding UC2ViewModel}" cal:View.Context="{Binding Items[1]}"/>
</ScrollViewer>
<ScrollViewer Grid.Row="1" Grid.Column="0" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<ContentControl cal:View.Model="{Binding UC3ViewModel}" cal:View.Context="{Binding Items[2]}"/>
</ScrollViewer>
<ScrollViewer Grid.Row="1" Grid.Column="1" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<ContentControl cal:View.Model="{Binding UC4ViewModel}" cal:View.Context="{Binding Items[3]}"/>
</ScrollViewer>
</Grid>
</Window>
For simplification each UserControl ViewModel and View are Identical:
UC#ViewModel:
namespace ContentControlTest.ViewModels
{
public class UC1ViewModel : Screen
{
private string id;
public string ID
{
get { return id; }
set
{
id = value;
NotifyOfPropertyChange(() => ID);
}
}
public UC1ViewModel()
{
ID = Guid.NewGuid().ToString();
}
}
}
UC#View:
<UserControl x:Class="ContentControlTest.Views.UC1View"
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:ContentControlTest.Views"
xmlns:cal="http://www.caliburnproject.org"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
>
<Border BorderBrush="Black" BorderThickness="1">
<StackPanel >
<TextBlock Text="{Binding DisplayName}"/>
<TextBlock Text="{Binding ID}"/>
</StackPanel>
</Border>
</UserControl>
For testing I have tried using an ItemControl and it works but doesn't give me exactly what I want.
<ItemsControl x:Name="Items">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
You need to create properties in your ShellViewModel something like UC1, UC2, UC3 etc. You then need to change your ShellView to bind to UC1 property.
<ContentControl x:Name="UC1" />
...
Caliburn Micro should do the plumbing for you.
namespace ContentControlTest.ViewModels
{
public class ShellViewModel : Conductor<object>.Collection.AllActive
{
// Modify to implement INotifyPropertyChanged event...
public UC1ViewModel UC1 { get; set }
public ShellViewModel()
{
UC1 = new UC1ViewModel();
ActivateItem(UC1);
ActivateItem(new UC2ViewModel());
ActivateItem(new UC3ViewModel());
ActivateItem(new UC4ViewModel());
}
}
}
The Caliburn concept of Context is used to map a view model to multiple views, usually through conventions and mapping namespaces. In this case, however, each of your view models maps to exactly one view. Hence you don't need to / should not provide a context.
Second, your view model binding cannot be resolved without exposing them as public props (as #Jack suggested). Ironically, the binding you used for Context is the right one for the view model binding.
Replacing
<ContentControl cal:View.Model="{Binding UC1ViewModel}" cal:View.Context="{Binding Items[0]}"/>
With
<ContentControl cal:View.Model="{Binding Items[0]}"/>
Should do the trick.
Given the number of items is fixed it's better to follow #Jack's approach and reference the view models in a strongly typed fashion. Rather than relying on their index in the items collection. You can use either:
<ContentControl cal:View.Model="{Binding UC1ViewModel}" />
Or
<ContentControl x:Name="UC1ViewModel" />
Which are synonymous.
As you noticed the Caliburn Conductor really shines when used in combination with ItemControl. You typically don't need to have strongly typed references to the each of the Items then. That doesn't mean you can't use the conductor as you did, you still enjoy all the benefits of the managed lifecycle.
In case anyone is having an issue implementing the [perfectly fine] accepted answer, here is a more in depth answer:
Your main window that contain both (or even more than two) of your User Controls must be inherited from Caliburn.Micro.Conductor<Screen>.Collection.AllActive;
Your User Controls must be inherited from Caliburn.Micro.Screen;
You must also keep naming conventions in mind. If you use MenuUC as the name of a ContentControl in your View, also create a property named MenuUC in your ViewModel;
Initialize your UserControl as I do in Constructor;
Now you can use ActivateItem(MenuUC) and DeactivateItem(MenuUC) everywhere in your code. Caliburn.Micro automatically detects which one you want to work with.
Example XAML View code:
<Window x:Class="YourProject.Views.YourView"
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"
mc:Ignorable="d"
Title="YourViewTitle" Width="900" Height="480">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="4*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Menu Side Bar -->
<ContentControl Grid.Row="0" Grid.Column="0" x:Name="MenuUC" />
<!-- Panel -->
<Border Grid.Column="1" Grid.RowSpan="2" BorderThickness="1,0,0,0" BorderBrush="#FF707070" >
<ContentControl x:Name="PanelUC" />
</Border>
</Grid>
</Window>
Example C# ViewModel code:
class YourViewModel : Conductor<Screen>.Collection.AllActive
{
// Menu Side Bar
private MenuUCViewModel _menuUC;
public MenuUCViewModel MenuUC
{
get { return _menuUC; }
set { _menuUC = value; NotifyOfPropertyChange(() => MenuUC); }
}
// Panel
private Screen _panelUC;
public Screen PanelUC
{
get { return _panelUC; }
set { _panelUC = value; NotifyOfPropertyChange(() => PanelUC); }
}
// Constructor
public YourViewModel()
{
MenuUC = new MenuUCViewModel();
ActivateItem(MenuUC);
PanelUC = new FirstPanelUCViewModel();
ActivateItem(PanelUC);
}
// Some method that changes PanelUC (previously FirstPanelUCViewModel) to SecondPanelUCViewModel
public void ChangePanels()
{
DeactivateItem(PanelUC);
PanelUC = new SecondPanelUCViewModel();
ActivateItem(PanelUC);
}
}
In the above example, ChangePanels() acts as a method to load new User Control into your ContentControl.
Also read this question, it might be help you further.

UserControl child fill Window

I have a UserControl that is a portion of a wpf window.
<Window>
<Grid>
<!--some other display elements would be here-->
<local:MyUserControl x:Name="Foo" Padding="0,42,0,50"/>
</Grid>
</Window>
Inside MyUserControl I have an element that is a gallery that is normally hidden, but when visible, it should fill the entire screen.
<UserControl>
<Grid>
<!--main display elements would be here-->
<Grid Name="Gallery" Visibility="Hidden">
<Rectangle Fill="Black" Opacity="0.75"/>
<TextBlock Name="GalleryLabel" Foreground="White" TextAlignment="Center">Current Image Title</TextBlock>
<Button Name="CloseGallery" Style="{DynamicResource WhiteTextButton}" Margin="0,0,0,0" Height="25" VerticalAlignment="Top" HorizontalAlignment="Right" Width="25" Click="GalleryClose_OnClick">X</Button>
<Image Name="GalleryImage" Margin="25"/>
</Grid>
</Grid>
</UserControl>
How can I set the Gallery to fill the entire Window rather than just the UserControl?
I was able to get it to work by adding Margin="0,-42,0,-50" to Gallery, but I don't like that solution. I would rather do something that doesn't involve hard-coding values in the UserControl so that I would be able to have more flexiblility in using it.
Normally it looks like:
where the green Foo area is MyUserControl, and the rest of the things in the window are other elements.
At certain points, I have a gallery display an image, which should fill the entire screen like:
which should fill the entire screen and have a black opaque overlay, along with an image displayed on top of the overlay.
Remove the Padding...
You can use:
<local:MyUserControl x:Name="Foo" VerticalAlignment="Stretch" HorizontalAligment="Stretch"/>
EDIT
I may admit that I still am not sure what you want. But I made something which does the same as you showed in the pictures. But be aware that this as an opinion based question, and the answer is my opinion, there are a lot of ways to achieve this. And this is only one of them:
MainWindow
<Window x:Class="MyProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myProject="clr-namespace:MyProject"
Title="MainWindow" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Background="Gainsboro" Grid.Row="0">
<StackPanel Orientation="Horizontal">
<Button Content="Show gallery" Width="100" Margin="10"/>
<Button Content="Do something else" Width="150" Margin="10"/>
</StackPanel>
</Border>
<myProject:SomeOtherStuff Grid.Row="1" />
<Border Grid.Row="2" Background="Gainsboro">
<StackPanel Orientation="Horizontal">
<Label Content="Buttom area, can be used for something else"/>
</StackPanel>
</Border>
<myProject:GalleryUserControl x:Name="GalleryUserControl" Grid.Row="0" Grid.RowSpan="3" Visibility="Hidden"/>
</Grid>
SomeOtherStuff UserControl
<UserControl x:Class="MyProject.SomeOtherStuff"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid Background="Green">
<Label VerticalAlignment="Center" HorizontalAlignment="Center" Content="Foo" FontSize="30" FontFamily="Verdana"/>
</Grid>
Gallery UserControl
<UserControl x:Class="XamDataGrid.GalleryUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="500" Width="800">
<Grid Background="#99000000">
<TextBlock Text="Currect image title" Foreground="White" TextAlignment="Center" VerticalAlignment="Top" HorizontalAlignment="Stretch"/>
<Button Name="CloseGallery" Margin="0,0,0,0" Content="X" Height="25" VerticalAlignment="Top" HorizontalAlignment="Right" Width="25" Click="GalleryClose_OnClick"/>
<Image Margin="30"/>
</Grid>
An Adorner will be best for you. As it floats above everything else and remains outside the VisualTree too.
UserControl2
<UserControl2 ...>
<Grid>
<Grid Background="#FF709FA6" Opacity="0.3" Width="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}"
Height="{Binding ActualHeight, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}">
</Grid>
<Grid Width="600" Height="700">
<Grid.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF37DAEA" Offset="0"/>
<GradientStop Color="#FFE84242" Offset="1"/>
</LinearGradientBrush>
</Grid.Background>
<!-- Gallery controls -->
<Button Content="Welcome to Gallery" HorizontalAlignment="Left" IsHitTestVisible="True" Margin="112,126,0,0" VerticalAlignment="Top" Height="68" FontSize="48" Click="Button_Click_1"/>
<TextBlock HorizontalAlignment="Left" Margin="83,42,0,0" TextWrapping="Wrap" Text="UserControl2" VerticalAlignment="Top" Foreground="#FF1B0D0D"/>
</Grid>
</Grid>
</UserControl2>
UserControl1
Code :
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// get root Window
DependencyObject current = LogicalTreeHelper.GetParent(this);
while (!(current is Window))
current = LogicalTreeHelper.GetParent(current);
Window root = current as Window;
IEnumerable children = LogicalTreeHelper.GetChildren(root);
Panel p = null;
foreach (var child in children)
{
p = child as Panel; // get first container panel in Window
break;
}
// Create UserControl2 to add to the adorner
UserControl2 ctrlGallery = new UserControl2();
AdornerLayer layer = AdornerLayer.GetAdornerLayer(p);
GalleryAdorner adorner = new GalleryAdorner(p, ctrlGallery);
layer.Add(adorner);
}
}
public class GalleryAdorner : Adorner
{
private Control _child;
VisualCollection collection;
public GalleryAdorner(UIElement elem, Control child)
: base(elem)
{
collection = new VisualCollection(this);
_child = child;
collection.Add(_child);
}
protected override int VisualChildrenCount
{
get
{
return 1;
}
}
protected override Visual GetVisualChild(int index)
{
if (index != 0) throw new ArgumentOutOfRangeException();
return collection[0];
}
protected override Size MeasureOverride(Size constraint)
{
_child.Measure(constraint);
return _child.DesiredSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
_child.Arrange(new Rect(new Point(0, 0), finalSize));
return new Size(_child.ActualWidth, _child.ActualHeight);
}
}

Creating a WPF Window that allows zooming and panning

I want to create a Window that will hold several controls. However, I would like the user to be able to pan around and zoom in and out to see larger versions of those controls.
I don't even know where to begin looking.
I was going to start at ScaleTransform that responds to the use of the scroll button on the mouse but I am not sure if that is the best idea.
Just need a push in the right direction.
thanks!
This might be a good candidate for a Viewbox.
See here: http://msdn.microsoft.com/en-us/library/system.windows.controls.viewbox(v=vs.110).aspx
Basically, you can Wrap the entire contents of the Window into a Viewbox like so:
<Window>
<Viewbox>
<!-- content here -->
</Viewbox>
</Window>
and then bind to the Viewbox control's width and height to simulate the zooming. For a quick test, you could just listen to scroll wheel events via code-behind, name the Viewbox control, and access the Viewbox directly at change the values there.
Edit: here's a scenario I just found to get you started. They are using an image, but it's the exact same concept that I described above.
http://www.c-sharpcorner.com/uploadfile/yougerthen/working-with-wpf-viewbox-control/
Edit2: Quick working example using mouse-scroll
Xaml:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
MouseWheel="MainWindow_OnMouseWheel">
<Grid>
<Viewbox x:Name="ZoomViewbox" Stretch="Fill">
<StackPanel>
<Label Content="Label" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Button Content="Button" HorizontalAlignment="Left" VerticalAlignment="Top" />
</StackPanel>
</Viewbox>
</Grid>
</Window>
C#:
using System.Windows;
using System.Windows.Input;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ZoomViewbox.Width = 100;
ZoomViewbox.Height = 100;
}
private void MainWindow_OnMouseWheel(object sender, MouseWheelEventArgs e)
{
UpdateViewBox((e.Delta > 0) ? 5 : -5);
}
private void UpdateViewBox(int newValue)
{
if ((ZoomViewbox.Width >= 0) && ZoomViewbox.Height >= 0)
{
ZoomViewbox.Width += newValue;
ZoomViewbox.Height += newValue;
}
}
}
}
You can get functionality out of a ScrollViewer and a ScaleTransform. Here's an example:
<Window x:Class="CSharpWpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- This ScrollViewer enables the panning -->
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<!-- This StackPanel is the container for the zoomable/pannable content. -->
<!-- Any container control (StackPanel, DockPanel, Grid, etc) may be used here. -->
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top">
<!-- This ScaleTransform implements the zooming and is bound the Value of the ZoomSlider -->
<StackPanel.LayoutTransform>
<ScaleTransform ScaleX="{Binding ElementName=ZoomSlider, Path=Value}" ScaleY="{Binding ElementName=ZoomSlider, Path=Value}" />
</StackPanel.LayoutTransform>
<!-- Here is your custom content -->
<Button>Foo</Button>
<Button>Bar</Button>
</StackPanel>
</ScrollViewer>
<!-- This Slider controls the zoom level -->
<Slider x:Name="ZoomSlider" Orientation="Horizontal" Grid.Row="1" Minimum="0.0" Maximum="8.0" LargeChange="0.25" SmallChange="0.01" Value="1.0" />
</Grid>
</Window>
Simple solution :
private void Window_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
if (Keyboard.Modifiers != ModifierKeys.Control)
return;
if (e.Delta < 0 && _scale > 0.7)
{
_scale -= 0.1;
MainGrid.LayoutTransform = new ScaleTransform(_scale, _scale);
}
else if (e.Delta > 0 && _scale < 1.5)
{
_scale += 0.1;
MainGrid.LayoutTransform = new ScaleTransform(_scale, _scale);
}
}

Dynamically generate textbox to match sum

I'm trying to generate Dynamic Text Box
Starting with two textboxes.
if the value is less than the value in the first textbox then generate another textbox dynamically and let the user enter more values.
This has to be done till the sum of the values of all the text boxes from the second to the last one generated becomes equal to the value of first textbox.
Of course other things need to be generated with the textboxes as well like lables etc. and positioned correctly so i thought of using a grid and generate the grid dynamically but above that i'm lost.
Any Help?
Thanks
i used a scrollviewer with the following code
<ScrollViewer Margin="8,8,8,14.417" Grid.Row="4" Grid.ColumnSpan="5" VerticalScrollBarVisibility="Hidden">
<Grid Margin="8" Grid.Row="4" Grid.ColumnSpan="4" x:Name="amtGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="103"/>
<ColumnDefinition Width="Auto" MinWidth="324"/>
<ColumnDefinition Width="Auto" MinWidth="218"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ComboBox x:Name="crdrnextrows" Margin="32.367,8,8,7.423" SelectedIndex="1" />
<ComboBox Background="#FFC6C3C6" Margin="8" x:Name="comboboxCr" GotKeyboardFocus="comboboxCr_GotKeyboardFocus" Grid.Column="1"/>
<TextBox Background="#FFC6C3C6" Foreground="White" IsEnabled="True" Margin="7.973,8,8,8" x:Name="txtBoxam1" Grid.Column="2" LostFocus="txtBoxam1_LostFocus"/>
<TextBox Background="#FFC6C3C6" Foreground="White" IsEnabled="True" Margin="8,8,33.972,8" x:Name="txtBoxamt2" Grid.Column="3" LostFocus="textBox4_LostFocus"/>
</Grid>
</ScrollViewer>
There is another textbox above that with similar code but without the scroll viewer, now what i was thinking was to dynamically create instances of the grid shown in the scrollviewer as many times as need to make them equal.
Is it possible to create new instances of the same grid and add them to the scollviewer dynamically with code?
Thanks
Looking at the additional information that you gave and given the complexity of the object you are wanting to create, a UserControl would probably be the best fit. This code is an example using a DoubleClick to show how to add the UserControl to your ScrollViewer. You will need to expose properties in the UserControl in order to get the information from your TextBoxes otherwise the Code should be simular to my earlier answer.
i.e:
UserControl Xaml
<UserControl x:Class="WpfApplication1.UserControl1"
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:DesignHeight="70" d:DesignWidth="985">
<Grid Margin="8" Grid.Row="4" Grid.ColumnSpan="4" x:Name="amtGrid" Height="40">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="103"/>
<ColumnDefinition Width="Auto" MinWidth="324"/>
<ColumnDefinition Width="Auto" MinWidth="218"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ComboBox x:Name="crdrnextrows" Margin="32.367,8,8,7.423" SelectedIndex="1" />
<ComboBox Background="#FFC6C3C6" Margin="8" x:Name="comboboxCr" GotKeyboardFocus="comboboxCr_GotKeyboardFocus" Grid.Column="1"/>
<TextBox Background="#FFC6C3C6" Foreground="White" IsEnabled="True" Margin="7.973,8,8,8" x:Name="txtBoxam1" Grid.Column="2" LostFocus="txtBoxam1_LostFocus"/>
<TextBox Background="#FFC6C3C6" Foreground="White" IsEnabled="True" Margin="8,8,33.972,8" x:Name="txtBoxamt2" Grid.Column="3" LostFocus="txtBoxamt2_LostFocus"/>
</Grid>
</UserControl>
Window Xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="123" Width="1098" xmlns:my="clr-namespace:WpfApplication1" MouseDoubleClick="Window_MouseDoubleClick">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel Name="stackPanel1" VerticalAlignment="top" HorizontalAlignment="Left" >
<my:UserControl1 x:Name="userControl11" Width="1077" />
</StackPanel>
</ScrollViewer>
</Window>
Main Window Code
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
UserControl1 newUserControl = new UserControl1();
newUserControl.Width = userControl11.Width;
newUserControl.Height = userControl11.Height;
stackPanel1.Children.Add(newUserControl);
}
}
Here is a basic idea, I am using a StackPanel to hold the TextBox's you may want to use a DockPanel to hold your labels and etc and add that to the StackPanel.
Xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel Name="myContainer">
<TextBox Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" KeyDown="textBox1_KeyDown" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="textBox2" VerticalAlignment="Top" Width="120" KeyDown="textBox2_KeyDown"/>
</StackPanel>
</Grid>
</Window>
Code
public partial class MainWindow : Window
{
Collection<Control> myControls = new Collection<Control>();
public MainWindow()
{
InitializeComponent();
myControls.Add(textBox2);
}
private void textBox2_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
int temp;
int sum;
sum = 0;
foreach (TextBox tb in myControls)
{
if (int.TryParse(tb.Text, out temp))
{
sum += temp;
}
}
int test = 0;
if (int.TryParse(textBox1.Text, out test))
{
if (sum < test)
{
TextBox newtb = new TextBox();
newtb.Width = ((TextBox)sender).Width;
newtb.Height = ((TextBox)sender).Height;
newtb.Margin = new Thickness(((TextBox)sender).Margin.Left, ((TextBox)sender).Margin.Top , ((TextBox)sender).Margin.Right , ((TextBox)sender).Margin.Bottom);
newtb.HorizontalAlignment = ((TextBox)sender).HorizontalAlignment;
newtb.KeyDown += new KeyEventHandler(textBox2_KeyDown);
myContainer.Children.Add(newtb);
myControls.Add(newtb);
newtb.Focus();
}
else
this.Background = Brushes.LightBlue;
}
}
}
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
textBox2.Focus();
}
}
}
Pass the collection to the item. Then in the set add to the collection as necessary.
public class dynamicInts
{
private int dInt;
private ObservableCollection<DynamicInt> dynamicInts
public int DInt
{
get { return dInt; }
set
{
value = dInt;
int sumInt;
foreach (DynamicInt di in dynamicInts) sumInt += di.Dint)
if (sumInt < 2*dynamicInts) dynamicInts.add(newdynamicInts ...

Silverlight 4: how to show ToolTip on keyboard focus (revised)

My original question:
Is there an easy way for a ToolTip to be shown when an item gets keyboard focus, not just mouse over? We have a list of items with tooltips that users will probably tab through, and the desired behavior is for a tooltip to be shown then too.
Added example XAML. A HyperlinkButton with the Tooltip set is what needs the keyboard focus as well.
<DataTemplate x:Key="OfferingItemDT">
<HyperlinkButton Command="{Binding Path=NavigateToLinkCommand}" ToolTipService.ToolTip="{Binding Tooltip}">
<Grid x:Name="gOfferingButtonRoot" Width="275" MaxHeight="78" Margin="5,3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Image x:Name="imgServiceOfferingIcon"
Grid.RowSpan="2"
VerticalAlignment="Top"
Source="{Binding Path=Image, Converter={StaticResource ByteArrayToImageConverter}}"
Stretch="UniformToFill"
Margin="2,10,0,0"
MaxHeight="32" MaxWidth="32"
/>
<TextBlock x:Name="txbOfferingTitle"
Grid.Column="1"
Grid.Row="0"
Text="{Binding Title}"
TextWrapping="Wrap"
Style="{StaticResource OfferingTileTitleText}"/>
<TextBlock x:Name="txbOfferingDesc"
Grid.Column="1"
Grid.Row="1"
Style="{StaticResource OfferingTileBodyText}"
Text="{Binding BriefDescription}" />
</Grid>
</HyperlinkButton>
</DataTemplate>
Updated:
Based on info in WPF: Show and persist ToolTip for a Textbox based on the cursor as well as Anthony's comments, I tried this code in the GotFocus eventhandler:
private void showTooltip(object sender, RoutedEventArgs e)
{
HyperlinkButton hb = new HyperlinkButton();
ToolTip ttip = new ToolTip();
hb = sender as HyperlinkButton;
ttip = ToolTipService.GetToolTip(hb) as ToolTip;
ttip.IsOpen = true;
}
This seems like it would work, but ttip is always null. Help?
"Easy" is subjective term. Yes its easy. On the same UI element on which you attach the ToolTip you can hook the GotFocus and LostFocus event handler the will use ToolTipService.GetToolTip to acquire the tooltip and the set IsOpen to true and false respectively.
The missing part is to define the tooltip in XAML so that we can access the Tooltip element.
<HyperlinkButton MouseLeftButtonUp="showTooltip">
<ToolTipService.ToolTip>
<ToolTip>
<TextBlock Text="My tooltip text"/>
</ToolTip>
</ToolTipService.ToolTip>
<!-- ... -->
</HyperlinkButton>
Code behind
private void showTooltip(object sender, RoutedEventArgs e)
{
FrameworkElement frameworkElement = (FrameworkElement)sender;
ToolTip tooltip = ToolTipService.GetToolTip(frameworkElement) as ToolTip;
if (tooltip != null)
{
tooltip.IsOpen = true;
frameworkElement.MouseLeave += new MouseEventHandler(frameworkElement_MouseLeave);
}
}
static void frameworkElement_MouseLeave(object sender, MouseEventArgs e)
{
FrameworkElement frameworkElement = (FrameworkElement)sender;
frameworkElement.MouseLeave -= new MouseEventHandler(frameworkElement_MouseLeave);
ToolTip tooltip = ToolTipService.GetToolTip(frameworkElement) as ToolTip;
if (tooltip != null)
{
tooltip.IsOpen = false;
}
}

Resources