I have two properties that need to be binded. One is Value and it is the UserControl's property and the second one is property from an image element. The image element is inside Window. The Window is intestated in the code behind (UserControl).
What do I need to add to enable proper binding between image src and property Value. The code is shown below.
<UserControl x:Class="nnnn">
<UserControl.Resources>
<propSheet:BitmapConverter x:Key="bitmapConverter" />
<Window x:Key="imagePopup"
Width="640"
Height="480">
<Grid Background="LightGray">
<Image Grid.Row="0" Source="{Binding Path=Value, Converter={StaticResource bitmapConverter}}" />
<TextBlock Text="{Binding Path=Value}"></TextBlock>
<Button Grid.Row="1"
Width="25"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
Click="OpenFile_Click">
...
</Button>
</Grid>
</Window>
</UserControl.Resources>
<Grid>
<Button Click="ViewImage_Click">View Image</Button>
</Grid>
You would have to set the Window's DataContext property to the UserControl instance before showing it:
private void ViewImage_Click(object sender, RoutedEventArgs e)
{
var window = Resources["imagePopup"] as Window;
window.DataContext = this; // the UserControl
window.Show();
}
Related
I have a user control, with the following (simlified) layout:
<UserControl x:Name="FV">
<DockPanel LastChildFill="False">
<StackPanel DockPanel.Dock="Left">
... some content, let's say customer name ...
</StackPanel>
<MyButton DockPanel.Dock="Left"
Visibility="{Binding Path=IsMouseOver, ElementName=FV, Converter={StaticResource boolToVisibilityConverter}}">
</MyButton>
</DockPanel>
</UserControl>
So basically it shows a text and on hover an edit button just to the right of this text.
Now I use this control as ItemTemplate in a tree.
The problem comes with long names. In this case the tree gets a horizontal scroll and UserControl logically stretches to the right and my button is not visible anymore.
text1 (edit) |
text22 (edit) |
vverylongtext |
I want to overlap the verylongtext on hover with my button:
text1 (edit) |
text22 (edit) |
vverylo(edit) |
How can I achieve this? My UserControl has no knowledge of where it is used and thus no knowledge of ActualWidth of parent elements.
I did it exactly according to your requirement. I created a UserControl with a TextBlock and Button. If text in TextBlock is very long, Button remains out of sight, which upon MouseOver comes int sight exactly as you need. However, if text in TextBlock remains small enough, Button remains in sight.
Note : HorizontalAlignment = Left must be set on the Button.
Window3.xaml
<Window x:Class="WpfStackOverflow.Window3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uc="clr-namespace:WpfStackOverflow"
Title="Window3" Height="300" Width="300" SizeToContent="WidthAndHeight">
<StackPanel>
<uc:UserControl1 Width="200" Height="35"/>
</StackPanel>
</Window>
UserControl.1.xaml
<UserControl x:Class="WpfStackOverflow.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"
xmlns:local="clr-namespace:WpfStackOverflow"
Background="Bisque"
Height="25">
<StackPanel x:Name="DckPnl" Height="25" Orientation="Horizontal">
<TextBlock x:Name="Tb" MouseEnter="Tb_MouseEnter_1" MouseLeave="Tb_MouseLeave_1" FontFamily="Arial" Text="some content , let's say customer name some content, let's say customer name" Background="AliceBlue"/>
<Button x:Name="Btn" Visibility="Hidden" Content="Edit" Width="35" Height="25" Margin="0 0 0 0" HorizontalAlignment="Left"/>
</StackPanel>
</UserControl>
UserControl1.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace WpfStackOverflow
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
private void Tb_MouseEnter_1(object sender, MouseEventArgs e)
{
Thickness newMargin = new Thickness();
FormattedText f = new FormattedText(Tb.Text,
new System.Globalization.CultureInfo("en-US"),
System.Windows.FlowDirection.LeftToRight,
new Typeface("Arial"),
Tb.FontSize, Brushes.Black);
if (f.Width > this.ActualWidth)
newMargin = new Thickness((this.ActualWidth - f.Width) - Btn.ActualWidth, 0, 0, 0);
else
newMargin = Btn.Margin;
Btn.Margin = newMargin;
Btn.Visibility = System.Windows.Visibility.Visible;
}
private void Tb_MouseLeave_1(object sender, MouseEventArgs e)
{
Btn.Margin = new Thickness(0, 0, 0, 0);
Btn.Visibility = System.Windows.Visibility.Hidden;
}
}
}
To place your button over the text you can use AdornerLayer, Z-Index, Tooltip or Popup. The last seems to me the easiest solution. Here is an example of how it can be done with popup:
<StackPanel Margin="0 20 0 0" Name="StackPanel" Width="100">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<ScrollViewer.Resources>
<wpfApplication1:OrConverter x:Key="OrConverter" />
</ScrollViewer.Resources>
<StackPanel Orientation="Horizontal">
<TextBlock Text="longlonglonglongtextlonglonglonglongtextlonglonglonglongtext" />
<Popup x:Name="Popup" StaysOpen="True"
PlacementTarget="{Binding ElementName=StackPanel}"
Placement="Right"
HorizontalOffset="-20"> <!--here you can bind to button's width instead of static value-->
<Popup.IsOpen>
<MultiBinding Converter="{StaticResource OrConverter}">
<Binding ElementName="StackPanel" Path="IsMouseOver" Mode="OneWay" />
<Binding ElementName="Popup" Path="IsMouseOver" Mode="OneWay" />
</MultiBinding>
</Popup.IsOpen>
<Button Name="Button" Content="X" Height="16" Width="20" VerticalAlignment="Top" />
</Popup>
</StackPanel>
</ScrollViewer>
</StackPanel>
OrConverter can be found in this answer.
And it looks like this.
I have a Window with a ListBox which has a DataTemplate, bound to an ObservableCollection of LogItems. The ItemsSource of the ListBox is set in code to the collection; the bindings on the TextBox and TextBlock which make up the DataTemplate are set in XAML. So far, so conventional. However, I need to set the font size/family for the TextBlock at runtime. Currently this information is held in a static cGlobals class. So I need to be able to bind the TextBlock.Text to the LogItems collection, but the TextBlock.FontSize property to the cGlobals.LogFontSize property. How can I do this, either via binding as sketched out in the XAML below, or in code?
<ListBox . . . . >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch" . . . . >
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="*" MinHeight="40" />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Background="Honeydew" Text="{Binding Mode=OneWay, Path=Header, . . . . />
<TextBlock FontSize="{Binding ??????}" Grid.Row="1" Text="{Binding Path=BodyText}" />
</Grid>
</DataTemplate >
</ListBox.ItemTemplate >
</ListBox>
xaml
<Window x:Class="WpfApplication6.StaticBinding"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication6"
Title="StaticBinding" Height="300" Width="300">
<Grid>
<TextBlock FontSize="{Binding Source={x:Static local:Global.FontSize}}" Text="abc"/>
</Grid>
Global
public class Global
{
public static double FontSize
{
get { return 20.0; }
}
}
You will need to declare a public property of Type cGlobals but the class cannot be static because you will need to use it as a return type. It does not look like you are following the Model-View-ViewModel pattern, since you are assigning the ItemsSource in the code-behind instead of XAML, so you will need to declare the property in the code-behind. In your Code-Behind(your .xaml.cs file)
private CGlobals _cGlobals;
public CGlobals CGlobals{get{return _cGlobals;}}
public CodeBehindConstructor(){
_cGlobals = new CGlobal{FontSize = 12, FontFamily="Times New Roman"};
}
xaml:
<Window Name="TheWindow">
<TextBlock FontSize="{Binding CGlobals.FontSize, ElementName=TheWindow}" Grid.Row="1" Text="{Binding Path=BodyText}" />
</Window>
As titled,
I seen couples of similiar question this or this in SO, but I don't see a solution for it.
I know if I need to bind to the code-beind, I need to set Datacontext = this
But my problem is that my datacontext already binding to my ViewModel, but I want to do some UI manipulation with using Command which is defined in the code-beind.
Is it possbile to bind it in xaml? If so, how?
EDIT: I did tried the follows:
<Window x:Class="WpfApplication3.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" x:Name="_Root">
<Grid x:Name="hellogrid">
<TextBlock x:Name="myTextBlock" Text="AAAA"/>
<Button Margin="82,119,121,120" Name="button2" Content="{Binding Path=Text, ElementName=myTextBlock}"/>
<Button Margin="82,72,121,0" Name="button3" Content="{Binding Path=MyText, ElementName=_Root}" Height="23" VerticalAlignment="Top" />
</Grid>
And code-behind:
public partial class Window1 : Window
{
public string MyText { get; set; }
public Window1()
{
InitializeComponent();
MyText = "ABC";
}
}
I could see the Button2 shows AAAA, but Button3 shows nothing....
Of course
There are many types of bindings. The most basic one binds to a property on the DataContext, which is usually inherited from a Parent object
<DataTemplate DataType="{x:Type MyModel}">
<!-- DataContext is object of type MyModel -->
<local:MyView />
</DataTemplate>
Or
<Window x:Name="MyWindow">
<!-- DataContext Inherited from Window -->
<TextBlock Text="{Binding SomeProperty}" />
</Window>
where
var SomeObject = new SomeModel();
SomeObject.SomeProperty = "Test";
myWindow.DataContext = SomeObject;
Other binding types include ElementName, where you can specify the target UI element to use as the data source for the binding
<StackPanel>
<CheckBox x:Name="SomeCheckBox" />
<TextBlock Text="{Binding ElementName=SomeCheckBox, Path=IsChecked}" />
</StackPanel>
or
<local:MyUserControl x:Name="SomeUserControl">
<Button Command="{Binding ElementName=SomeUserControl, Path=DataContext.SaveCommand}" />
</local:MyUserControl >
Or RelativeSource, which allows you to find an object relative to the current object to use as a DataSource
<Window Title="Test">
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=Title}" />
</Window>
or
<local:MyUserControl>
<Button Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:MyUserControl}}, Path=DataContext.SaveCommand}" />
</local:MyUserControl >
And TemplateBinding, which binds is a shortcut to a RelativeSource binding that binds to a templated object
<Button Content="Test">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<TextBlock Text="{TemplateBinding Content}" />
</ControlTemplate>
</Button.Template>
</Button>
EDIT
The best solution IMO is the one posted by #Saad Imran in this SO question...
With this solution all you have to do is name your window and binding to a property in your XAML will be as easy as this {Binding ElementName=MyWindowName, Path=MyText}
So, what you are doing with Content="{Binding Path=MyText, ElementName=_Root}" is exactly right and your Button Content property IS bound to MyText property but the only thing you are missing is change notification (need to implement INotifyPropertyChanged interface for that) so when you set your MyText property to ABC MyText = "ABC"; no change notification is sent...
Easy way to test this is by setting the MyText property explicitly as such:
private string myText = "ABC";
public string MyText
{
get { return myText; }
set { myText = value; }
}
or setting it in the constructor before InitializeComponent() is called:
MyText = "ABC";
InitializeComponent();
If you do that you'll notice that your button will have ABC as its content but changes to MyText property will not affect the button content because there is no change notification...
Sure, you can use ElementName:
<Window Name="root"
Class="..."
...>
...
<TextBox Text="{Binding Path=Foo, ElementName=root}" />
You could also do it with RelativeSource, but the syntax is uglier...
I'm trying to build a very simple and basic application that adds tab items to tab control using the MVVM pattern.
So i created:
a simple view with one button - "CustomerView.xaml"
an empty ViewModel class - it is empty cause the view doesn't save or extract any information from the Viewmodal (have only one button) - "CustomerViewModel.cs"
The MainWindow class code holds an observable collection of the CustomerViewModel
and have one "Add customer" button - to add a customer tab item to the tabcontrol and the tabcontrol itself.
i don't use commands cause it is not relevant at this time , i just was the new tabitem to appear when i add a new CustomerViewModel to the collection.
the result is that , although i can see that CustomerViewModels are added to the Observable collection, i still don't see tabitems added to the tabcontrol - The collection is not updating the the tabcontrol.
This is the MainWindow XAML:
<Window x:Class="MyViewModalTabControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:MyViewModalTabControl.ViewModal"
xmlns:vw="clr-namespace:MyViewModalTabControl.Views"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate DataType="{x:Type vm:CustomerViewModel}">
<vw:CustTabView />
</DataTemplate>
<DataTemplate x:Key="ClosableTabItemTemplate">
<DockPanel Width="120">
<Button
Content="X"
Cursor="Hand"
DockPanel.Dock="Right"
Focusable="False"
FontFamily="Courier"
FontSize="9"
FontWeight="Bold"
Margin="0,1,0,0"
Padding="0"
VerticalContentAlignment="Bottom"
Width="16" Height="16"
/>
<ContentPresenter
Content="Sample"
VerticalAlignment="Center"
/>
</DockPanel>
</DataTemplate>
</Window.Resources>
<Grid Margin="4" ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Name="CustTabButton" Content="New Customer" Height="30" Margin="12,136,9,136" Click="CustTabButton_Click"></Button>
<TabControl Grid.Column="1" Grid.Row="0" Background="Red"
ItemsSource="{Binding CustomerTabs}"
ItemTemplate="{StaticResource ClosableTabItemTemplate}"
>
</TabControl>
</Grid>
This is the code behind of the MainWindow:
public partial class MainWindow : Window
{
private ObservableCollection<CustomerViewModel> _customertabs;
public ObservableCollection<CustomerViewModel> CustomerTabs
{
get
{
if (_customertabs == null)
{
_customertabs = new ObservableCollection<CustomerViewModel>();
// _workspaces.CollectionChanged += this.OnWorkspacesChanged;
}
return _customertabs;
}
}
public MainWindow()
{
InitializeComponent();
}
private void CustTabButton_Click(object sender, RoutedEventArgs e)
{
CustomerViewModel CustomerWorkSpace = new CustomerViewModel();
this.CustomerTabs.Add(CustomerWorkSpace);
}
}
This is the Viewmodel class:
public class CustomerViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
}
This is the View :
UserControl x:Class="MyViewModalTabControl.Views.CustTabView"
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="300" d:DesignWidth="300">
<Grid>
<Button Name="CustTabButton" Content="New Customer" Height="30" Margin="12,136,9,136"></Button>
</Grid>
What am i missing ?
where do you set the datacontext for your mainwindow? Your bindings will just work with the right Datacontext.
and wouldn't it be better to create a mainviewmodel too, which handles the stuff you put in the mainwindow.cs at the moment?
EDIT: pls look at this msdn post from josh smith. there you can find a closable tab too.
Try any of the following
the ClosableTabItemTemplate should "return" TabItem that will be displayed in the Tab control not the DockPanel
create template for the TabItem control
do it in code
This is the fix:
public MainWindow()
{
InitializeComponent();
this.DataContext=this;
}
I'd better ask the question by example. Let's say I have UserControl and Window which uses this control.
I would like to design this control (named MyControl) in such way (this is sci-fi syntax!):
<Grid>
<Button>Just a button</Button>
<PlaceHolder Name="place_holder/>
</Grid>
and use in such ways when designing my Window:
<MyControl/>
or
<MyControl>
<place_holder>
<Button>Button 1</Button>
</place_holder>
</MyControl>
or
<MyControl>
<place_holder>
<Button>Button 1</Button>
<Button>Button 2</Button>
</place_holder>
</MyControl>
Of course I would like to have ability to add even more elements to MyControl in Window. So, in a way it should work as container (like Grid, StackPanel, and so on). The placement would be defined in UserControl (in this example after button "Just a button") but what to add (what elements) would be defined in Window (where UserControl -- MyControl -- is used).
I hope this is clear what I would like to achieve. The key point is using XAML when designing Window, so my class should be no worse than other controls.
Now, the big QUESTION is -- how to do it?
Remarks: styling is out of scope. All I want to do is add any controls I want to MyControl when designing Window (not when designing MyControl).
ContentControls & ItemsControls are good for this, you can bind them to a property of your UserControl or expose them.
Using a ContentControl (for placeholders in multiple disconnected places):
<UserControl x:Class="Test.UserControls.MyUserControl2"
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"
Name="control">
<Grid>
<Button>Just a button</Button>
<ContentControl Content="{Binding PlaceHolder1, ElementName=control}"/>
</Grid>
</UserControl>
public partial class MyUserControl2 : UserControl
{
public static readonly DependencyProperty PlaceHolder1Property =
DependencyProperty.Register("PlaceHolder1", typeof(object), typeof(MyUserControl2), new UIPropertyMetadata(null));
public object PlaceHolder1
{
get { return (object)GetValue(PlaceHolder1Property); }
set { SetValue(PlaceHolder1Property, value); }
}
public MyUserControl2()
{
InitializeComponent();
}
}
<uc:MyUserControl2>
<uc:MyUserControl2.PlaceHolder1>
<TextBlock Text="Test"/>
</uc:MyUserControl2.PlaceHolder1>
</uc:MyUserControl2>
ItemsControl-Version (for collections in one place)
<UserControl x:Class="Test.UserControls.MyUserControl2"
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"
Name="control">
<Grid>
<Button>Just a button</Button>
<ItemsControl Name="_itemsControl" ItemsSource="{Binding ItemsSource, ElementName=control}"/>
</Grid>
</UserControl>
[ContentProperty("Items")]
public partial class MyUserControl2 : UserControl
{
public static readonly DependencyProperty ItemsSourceProperty =
ItemsControl.ItemsSourceProperty.AddOwner(typeof(MyUserControl2));
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public ItemCollection Items
{
get { return _itemsControl.Items; }
}
public MyUserControl2()
{
InitializeComponent();
}
}
<uc:MyUserControl2>
<TextBlock Text="Test"/>
<TextBlock Text="Test"/>
</uc:MyUserControl2>
With UserControls you can decide to expose certain properties of internal controls; besides the ItemsSource one probably would want to also expose properties like the ItemsControl.ItemTemplate, but it all depends on how you want to use it, if you just set the Items then you do not necessarily need any of that.
I think you want to set your UserControl's ControlTemplate with a ContentPresenter located inside (so you can define where the Content will be presented).
Your Custom UserControl:
<UserControl x:Class="TestApp11.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Template>
<ControlTemplate>
<StackPanel>
<TextBlock Text="Custom Control Text Area 1" />
<ContentPresenter Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />
<TextBlock Text="Custom Control Text Area 2" />
</StackPanel>
</ControlTemplate>
</UserControl.Template>
</UserControl>
Usage:
<Window x:Class="TestApp11.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:TestApp11"
Title="Window1" Height="250" Width="200">
<StackPanel>
<l:UserControl1>
<Button Content="My Control's Content" />
</l:UserControl1>
</StackPanel>
</Window>
If you need multiple items in your content section, simply place them in a container like a grid or a stackpanel:
<l:UserControl1>
<StackPanel>
<Button Content="Button 1" />
<Button Content="Button 2" />
</StackPanel>
</l:UserControl1>