I created a custom silverlight UserControl. I need to be able to set its content through a "Child" property. So I used the "[ContentProperty("Child")]" class attribute :
[ContentProperty("Child")]
public partial class SizeableCheckBox : UserControl
{
public SizeableCheckBox()
{
InitializeComponent();
}
public object Child
{
get { return contentControl1.Content; }
set { contentControl1.Content = value; }
}
The XAML of the UserControl looks like that :
<Grid x:Name="LayoutRoot" >
<StackPanel Orientation="Horizontal">
<Border x:Name="brdCheck" />
<ContentControl x:Name="contentControl1" />
</StackPanel
...
</Grid>
Now if I use my UserControl in my application everything works fine (even in VS2010 design mode) :
<my:SizeableCheckBox x:Name="chkTestCheck">
<StackPanel Orientation="Horizontal">
<Image ... />
<Textblock x:Name="txtCheckBoxTest" Text="My Checkbox test" />
</StackPanel>
</my:sizeableCheckBox>
But in my code I have a reference to the "txtCheckBoxTest" but that object is null on runtime. What am I doing wrong?
Thanks
you should be able to go chkTestCheck.txtCheckBoxTest.Text
unless i am misunderstanding the quesiton
Related
I am trying to insert a datagrid into popup, but it is't not working correctly (popup contains empty datagrid). I've tried to put my datagrid outside popup and it's worked. I guess popup behaves like seperate window, so I wonder if I supposed to create another ViewModel class for my popup, or is there another way to solve that?
My View xaml code:
<UserControl x:Class="MyApplication.Views.AddArrivalView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-MyApplication.Views" >
<Grid>
<StackPanel Orientation="Horizontal" Margin="5">
//<Some textblocks>
</StackPanel>
<Popup Width="700" Height="300" IsOpen="true">
<DataGrid x:Name="SuggestedCars"
AutoGenerateColumns="true"
IsReadOnly="True">
</DataGrid>
</Popup>
</Grid>
</UserControl>
My ViewModel
namespace MyApplication.ViewModels
{
class AddArrivalViewModel : PropertyChangedBase
{
private BindableCollection<Cars> _suggestedCars;
public BindableCollection<Cars> SuggestedCars
{
get
{
return _suggestedCars;
}
set
{
_suggestedCars = value;
NotifyOfPropertyChange("SuggestedCars");
}
}
public AddArrivalViewModel()
{
SuggestedCars = new BindableCollection<Cars>();
//add some test cars to collection
SuggestedCars.Add(new Cars() { Brand = "Opel", Model = "Corsa", Registration = "000000", ID = 0 });
}
}
}
I have a very simple sample code, a TextBox and a Button. User inputs some text in textBox and click the Button. It should show a common MessageBox which contains the text he just input in the TextBox.
Problem: the MessageBox shows blank! After some trial and error I found out the reason. I set in .xaml the Focusable of Button as false. Why? Because I want to stop the blinking effect of button after it is clicked once.
So I have an idea. I bind the Focusable to a property in ViewModel and initialized in constructor (or in private field as initial value) of ViewModel as false. Then in the Button-Click event handler before showing MessageBox I set the property to true and then after executing MessageBox set it to false again. Problem: Unfortunately, it doesn't work! It seems the Focusable can only be set once in constructor or in private field as initial value.
Does someone know how to solve this problem? I want to keep the Focusable of the Button to false (to avoid the Button blinking) and I want to get the text from TextBox by clicking the Button. Following is the sample code and feel free to modify and show me the solution. Thank you in advance.
XAML / View
<Grid Width="300" Height="300">
<StackPanel VerticalAlignment="Center">
<TextBox Width="150" Text="{Binding Path=MyText}" />
<Button x:Name="ShowText"
Width="100"
Content="Show Text"
Focusable="{Binding Path=MyFocus}" />
</StackPanel>
</Grid>
View model
public class ShellViewModel : PropertyChangedBase
{
private String _myText;
public String MyText
{
get { return _myText; }
set
{
_myText = value;
NotifyOfPropertyChange(() => MyText);
}
}
private Boolean _myFocus = false; // if it is true, the MessageBox can show MyText, otherwise MessageBox shows blank
public Boolean MyFocus
{
get { return _myFocus; }
set
{
_myFocus = value;
NotifyOfPropertyChange(() => MyFocus);
}
}
public void ShowText()
{
//MyFocus = true; // this doesn't work
MessageBox.Show(MyText);
//MyFocus = false; // this doesn't work
}
}
<Grid Width="300" Height="300">
<StackPanel VerticalAlignment="Center">
<TextBox Width="150" Text="{Binding MyText, Mode=TwoWay}" />
<Button x:Name="ShowText"
Width="100"
Content="Show Text"
Focusable="{Binding MyFocus}" />
</StackPanel>
</Grid>
or
<Grid Width="300" Height="300">
<StackPanel VerticalAlignment="Center">
<TextBox Width="150" x:Name="MyText" />
<Button x:Name="ShowText"
Width="100"
Content="Show Text"
Focusable="{Binding MyFocus}" />
</StackPanel>
</Grid>
that should work either way, the way you did the binding wouldn't ever update the underlying property since it wasn't considered bi-directional. Therefore Mode=TwoWay was necessary on the 1st version, the second version uses CM's conventions to find the the textbox and bind it to a property of the same name on the viewmodel.
not sure what blinking your referring to the default style doesn't have a blink... at least not on windows 8.1 or windows 10. The only thing focusable does in the regard of the code frag you did was prevent keyboard access..
Essentially, I have a markup issue. I have come up with a few solutions but I can't help but feel like this should be simpler. Rather than lead you down my convoluted path I thought I would share the simplest implementation and ask how you would address it.
MainPage.xaml
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="6" />
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="6" />
<ColumnDefinition />
<!--Additional Columns-->
</Grid.ColumnDefinitions>
<!--Row Definitions-->
<Label Grid.Row="0" Grid.Column="0" Content="Vin:" HorizontalAlignment="Right" />
<ctrl:CommandTextBox Grid.Row="0" Grid.Column="2" Command="{Binding CreateVehicleCommand}" CommandParameter="{Binding Text, RelativeSource={RelativeSource Self}}" />
<Label Grid.Row="0" Grid.Column="3" Content="Manufacturer:" HorizontalAlignment="Right" />
<TextBox Grid.Row="0" Grid.Column="5" IsEnabled="False" Text="{Binding Vehicle.Manufacturer, Mode=OneWay}" />
<!--Additional Read Only Values-->
</Grid>
Given the example above, how can I get the Contents of the Grid into a View given the constraint that the Command to create the vehicle is outside of the DataContext to be created(Vehicle)?
If you do wish to look at my specific attempt, that question is here UserControl's DependencyProperty is null when UserControl has a DataContext
how can I get the Contents of the Grid into a View given the
constraint that the Command to create the vehicle is outside of the
DataContext to be created(Vehicle)?
This feels like a race condition more than an MVVM problem. I will address the issue first but make a secondary suggestion after.
There are no reasons in which a ViewModel cannot contain another viewmodel as a reference and that reference is bound to using the INotifyPropertyChanged mechanisim.
Or that your xaml (view) page contains a static reference to a ViewModel which the page (view) does not directly use in its DataContext, but that a certain control cannot bind to that static outside of the data context of the containing control.
Either way one can provide access (as also mentioned in the response to the other post you provided) by pointing to itself to get data or to provide an alternate plumbing which gets the data.
Or you can flatten your viewmodel to contain more information and handle this IMHO race condition so that this situation doesn't arise and the control as well as the grid can access information in a proper format.
I can't fully address the problem because you are more aware of the design goals and hazards which now must be worked around.
I've come up with something, I'm relatively happy with. This has saved me from creating 100s of composite ViewModel's and while it does introduce some unnecessary complexity it does dramatically reduce the amount copy/paste code I need to write.
VMFactoryViewModel.cs
public class CreatedViewModelEventArgs<T> : EventArgs where T : ViewModelBase
{
public T ViewModel { get; private set; }
public CreatedViewModelEventArgs(T viewModel)
{
ViewModel = viewModel;
}
}
public class VMFactoryViewModel<T> : ViewModelBase where T : ViewModelBase
{
private Func<string, T> _createViewModel;
private RelayCommand<string> _createViewModelCommand;
private readonly IDialogService _dialogService;
/// <summary>
/// Returns a command that creates the view model.
/// </summary>
public ICommand CreateViewModelCommand
{
get
{
if (_createViewModelCommand == null)
_createViewModelCommand = new RelayCommand<string>(x => CreateViewModel(x));
return _createViewModelCommand;
}
}
public event EventHandler<CreatedViewModelEventArgs<T>> CreatedViewModel;
private void OnCreatedViewModel(T viewModel)
{
var handler = CreatedViewModel;
if (handler != null)
handler(this, new CreatedViewModelEventArgs<T>(viewModel));
}
public VMFactoryViewModel(IDialogService dialogService, Func<string, T> createViewModel)
{
_dialogService = dialogService;
_createViewModel = createViewModel;
}
private void CreateViewModel(string viewModelId)
{
try
{
OnCreatedViewModel(_createViewModel(viewModelId));
}
catch (Exception ex)
{
_dialogService.Show(ex.Message);
}
}
}
VMFactoryUserControl.cs
public class VMFactoryUserControl<T> : UserControl where T : ViewModelBase
{
public static readonly DependencyProperty VMFactoryProperty = DependencyProperty.Register("VMFactory", typeof(VMFactoryViewModel<T>), typeof(VMFactoryUserControl<T>));
public VMFactoryViewModel<T> VMFactory
{
get { return (VMFactoryViewModel<T>)GetValue(VMFactoryProperty); }
set { SetValue(VMFactoryProperty, value); }
}
}
GenericView.xaml
<ctrl:VMFactoryUserControl x:Class="GenericProject.View.GenericView"
x:TypeArguments="vm:GenericViewModel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ctrl="clr-namespace:SomeProject.Controls;assembly=SomeProject.Controls"
xmlns:vm="clr-namespace:GenericProject.ViewModel">
<Grid>
<!-- Column Definitions -->
<!-- Row Definitions -->
<Label Grid.Row="0" Grid.Column="0" Content="Generic Id:" HorizontalAlignment="Right" />
<ctrl:CommandTextBox Grid.Row="0" Grid.Column="2"
Command="{Binding VMFactory.CreateViewModelCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
CommandParameter="{Binding Text, RelativeSource={RelativeSource Self}}" />
<Label Grid.Row="0" Grid.Column="3" Content="Generic Property:" HorizontalAlignment="Right" />
<TextBox Grid.Row="0" Grid.Column="5" IsEnabled="False" Text="{Binding GenericProperty, Mode=OneWay}" />
<!--Additional Read Only Values-->
</Grid>
</ctrl:VMFactoryUserControl>
GenericView.xaml.cs
public partial class GenericView : VMFactoryUserControl<GenericViewModel>
{
public GenericView()
{
InitializeComponent();
}
}
MainPageViewModel.cs
public class MainPageViewModel : ViewModelBase
{
private readonly IDialogService _dialogService;
private GenericViewModel _generic;
private readonly VMFactoryViewModel<GenericViewModel> _genericFactory;
public GenericViewModel Generic
{
get { return _generic; }
private set
{
if (_generic != value)
{
_generic = value;
base.OnPropertyChanged("Generic");
}
}
}
public VMFactoryViewModel<GenericViewModel> GenericFactory
{
get { return _genericFactory; }
}
private void OnGenericFactoryCreatedViewModel(object sender, CreatedViewModelEventArgs<GenericViewModel> e)
{
Generic = e.ViewModel;
}
public MainPageViewModel(IDialogService dialogService)
{
_dialogService = dialogService;
_genericFactory = new VMFactoryViewModel<GenericViewModel>(_dialogService, x => new GenericViewModel(_dialogService, GetGeneric(x)));
_genericFactory.CreatedViewModel += OnGenericFactoryCreatedViewModel;
}
private Generic GetGeneric(string genericId)
{
// Return some Generic model.
}
}
MainPage.xaml
<Page x:Class="GenericProject.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vw="clr-namespace:GenericProject.View">
<StackPanel>
<!-- Headers and Additional Content. -->
<vw:EventView DataContext="{Binding Generic}"
VMFactory="{Binding DataContext.GenericFactory, RelativeSource={RelativeSource AncestorType={x:Type Page}}}" />
</StackPanel>
</Page>
I have three Text Box called TxtDocumentTitle1, TxtDocumentTitle2,TxtDocumentTitle3 lastly there is a Add More Button. Client can Click Add more Button so that it generates Text box naming TxtDocumentTitle4. If more needed he/she can Add more Text Boxes.
Sample XAML code of View
<Grid Height="450" Width="700" Background="White">
<TextBlock Height="23" HorizontalAlignment="Left" Margin="67,20,0,0" Name="textBlocKname" Text="Document Title1:" VerticalAlignment="Top" Width="110" />
<TextBlock Height="23" HorizontalAlignment="Left" Margin="67,87,0,0" Name="textBlockAddress" Text="Document Title2:" VerticalAlignment="Top" Width="110" />
<TextBlock Height="23" HorizontalAlignment="Left" Margin="67,154,0,0" Name="textBlockCompanyName" Text="Document Title3:" VerticalAlignment="Top" Width="110" />
<TextBox Height="46" Margin="67,37,87,0" Name="txtDocumentTitle1" VerticalAlignment="Top" FontSize="24" />
<TextBox Height="46" HorizontalAlignment="Left" Margin="67,106,0,0" Name="txtDocumentTitle3" VerticalAlignment="Top" Width="546" FontSize="24" />
<TextBox Height="46" HorizontalAlignment="Left" Margin="67,171,0,0" Name="txtDocumentTitle2" VerticalAlignment="Top" Width="546" FontSize="24" />
<Button Content="Add More" Height="37" HorizontalAlignment="Right" Margin="0,223,87,0" Name="btnAddmore" VerticalAlignment="Top" Width="102" />
</Grid>
You can achieve this easily via Binding. if your Window does not have a ViewModel open your window's xaml.cs and make it like this:
public Window1()
{
InitializeComponent();
DataContext = this;
}
public ObservableCollection<TextBoxVm> Items { get { return _items; } }
private ObservableCollection<TextBoxVm> _items = new ObservableCollection<TextBoxVm>();
if not, just add the two last lines to the viewModel of your window.
Now you need to define a class derived from DependencyObject and name it say TextBoxVm. create two DependencyPropertys in it (use propdp snippet) as follows:
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(TextBoxVm), new UIPropertyMetadata("default text",
(d,e)=>
{
var vm = (TextBoxVm)d;
var val = (string)e.NewValue;
MyDataService.FindAndUpdateItemInDatabase(vm.Id, val);//you can access database with something like this
}));
public string TitleText
{
get { return (string)GetValue(TitleTextProperty); }
set { SetValue(TitleTextProperty, value); }
}
public static readonly DependencyProperty TitleTextProperty =
DependencyProperty.Register("TitleText", typeof(string), typeof(TextBoxVm), new UIPropertyMetadata("default title"));
This would be the xaml code:
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding TitleText}"/>
<TextBox Text="{Binding Text}"/>
</WrapPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Now the only thing left is to write Button logic. simply add TextBoxVm to Items when Button is clicked.
Items.Add(new TextBoxVm {
TitleText = string.Format("Document Title{0}:", Items.Count+1)
});
Edit Note:
this approach is standard MVVM (expect for the button click event, which should be done using Command). So if you want to add controls in code (which is not recommended) search this :
add control to wpf grid programmatically.
*Above Answer from Bizz Gives Solution of My Question * Beside that it Rise me a Question about *DependencyObject * after Few Research i found this about Dependancy Object which may be Helpful for New comer to WPF like me :)
What is DependencyObject??
Dependency object is the base object for all WPF objects. All the UI Elements like Buttons TextBox etc and the Content Elements like Paragraph, Italic, Span etc all are derived from Dependency Object.
Dependency objects are used for WPF property system. By default, what ever the property system we have in DOT Net CLR is very basic. But Dependency properies provide lots of addtional features/services to support Data Binding.
Once you create any property as a dependency property, then automatically you get following feature implemented for you. ie. Change Notification, Validation, Call Back, Inheritance, DataBinding, Styles, Default Values etc.
If you need to implement all these features on your own for all properties where you need these feature, then it will be a big process and head ache for you. So, these all coming out of the box from Dependency Object class.
Basically dependency object class contains a dictionary. So, when ever set any value or retrieve value, then it will change the value or read from that Dictionary. So, it is nothing but a key value pair.
For Detail Info abouT DependencyObject
http://www.codeproject.com/Articles/140620/WPF-Tutorial-Dependency-Property
http://www.pinfaq.com/32/what-is-dependency-object-in-wpf-where-should-i-use-it
I am attempting to bind Image.Source in a DataTemplate to a System.Drawing.Image as discussed here: using XAML to bind to a System.Drawing.Image into a System.Windows.Image control
<UserControl.Resources>
<media:ImageConverter x:Key="imageConverter" />
<DataTemplate DataType="{x:Type data:GameTile}" >
<StackPanel Orientation="Vertical" Margin="5" Background="Transparent">
<Viewbox>
<TextBlock FontWeight="Bold" Text="{Binding PointValue}" TextAlignment="Center" FontSize="14" />
</Viewbox>
<Image Margin="0,5,0,0" Source="{Binding Path=Image.Image, Converter={StaticResource imageConverter}}" />
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid>
<loop:ListBox x:Name="listBox1"
ItemsSource="{Binding Path=GameModel.Game.GameTiles}"
ItemContainerStyle="{StaticResource GameTileContainerStyle}" Orientation="Vertical" />
</Grid>
The GameTile object has an Image (not a system.drawing.image) property that points to a Picture object which has an Image property of type System.Drawing.Image.
I am binding the ItemsSource on the ListBox to a GameTiles Collection on a Game object.
Objects
public class Game
{
public XPCollection<GameTile> GameTiles
{
get { return GetCollection<GameTile>("GameTiles"); }
}
}
public class GameTiles
{
Picture fImage;
public Picture Image
{
get { return fImage; }
set { SetPropertyValue<Picture>("Image", ref fImage, value); }
}
}
public class Picture
{
private FileData fFile;
public FileData File
{
get { return fFile; }
set
{
SetPropertyValue("File", ref fFile, value);
if (string.IsNullOrEmpty(fName))
{
fName = (value == null ? string.Empty : value.FileName);
}
fImage = null;
}
}
Image fImage;
public System.Drawing.Image Image
{
get
{
if (fImage == null)
{
try
{
MemoryStream stream = new MemoryStream();
fFile.SaveToStream(stream);
stream.Position = 0;
fImage = Image.FromStream(stream);
}
catch
{
//TODO: log exception
}
}
return fImage;
}
//set { SetPropertyValue<Image>("Image", ref fImage, value); }
}
}
The images are not showing up in the ListBoxItems, but any other property that I bind to in the DataTemplate will show up. It may be worth noting that I am using Devexpress Xpo as an ORM. Also the classes represented above do implement INotifyPropertyChanged.
Any thoughts on what I may be missing?
EDIT: Forgot to mention that I have implemented a value converter as mentioned in the post that I linked to above. However, if I put a breakpoint in the converter method, it is never called.
EDIT: Added the fFile property to the code above.
I can set an Image.Source to the GameTile.Image.Image property through c#(by converting it to BitmapImage), and have it work as expected, but I'm not sure how to accomplish that with a DataTemplate through c#. I would prefer to set the binding in XAML, but would settle for a c# workaround with a DataTemplate (or something else that would work). I am pretty confident that the issue is not with the GameTile.Image property pulling image from the database because if I manually set the source on an Image.Source in c#, the image is there. It simply isn't working in the DataTemplate.
Edit: Determined the issue to be related to properties that are not directly on the DataType that I am binding to for example with and GameTile has a (int)PointValue property, a (Picture object)Image property, and a (Prize object)Prize property.
If I bind to
<TextBlock Text="{Binding PointValue}" />
it works as expected.
But if I bind to
<TextBlock Text="{Binding Prize.Name}" />
it does not work.
And If I bind to
<Image Margin="0,5,0,0" Source="{Binding Image.BitmapImage}" />
it fails also. The following graphic shows the error that is being thrown by the binding.
BindingExpression path error: 'Name' property not found on 'object'
''XPCollection' (Hash=...)'. BindingExpression:Path=Prize.Name;
DataItem=GameTile' (HashCode=...); target element is
'TextBlock'(Name=''); target property is 'Text' (type 'String')
Thanks,
Eric
Found the solution.
Had to change this:
<TextBlock Text="{Binding Prize.Name}" />
to this:
<TextBlock Text="{Binding Prize!.Name}" />
The only difference is the exclamation point(!).
This also worked for the image property.
<Image Margin="0,5,0,0" Source="{Binding Path=Image!.Image, Converter={StaticResource imageConverter}}" />