I am trying to get colorpicker working for my program and I'm having trouble with it keeping the value on reinitialization. I was wondering if I'm missing something or if popupcoloredit initialization is overwriting the value. On viewmodel initialization it keeps the strings and bools I have but the Colors are reset without the setter being called so I think it's being destroyed.
My XAML is as follows:
<TextBlock Text="{x:Static meta:MetaCommon.Text}" Style="{StaticResource ContentHeader}" TextWrapping="Wrap" />
<dxe:PopupColorEdit Name="TextColour" Text="{Binding TextColour}" MinWidth="130" Margin="0,0,0,10" />
I then use it in the textbox
<TextBox Text="Sample text..." Margin="0,0,0,10" Name="TextBox1"
Foreground="{Binding Path=Color, ElementName=TextColour, Converter={StaticResource ColorToBrushConverter}}"
Background="{Binding Path=Color, ElementName=BackgroundColour, Converter={StaticResource ColorToBrushConverter}}" />
C# is simply a get set
public static Color TextColour { get; set; }
Don't bind to PopupColorEdit.Text. Bind to PopupColorEdit.Color. And because it's DevExpress, always be on the lookout for their habit of neglecting to set BindsTwoWaysByDefault = true on dependency properties where they should. This is one such property. By default, the Color property of PopupColorEdit never updates the property you bind to it. This is correct approximately never, but it's the default they shipped it with.
<dxe:PopupColorEdit
Name="TextColour"
Color="{Binding TextColour, Mode=TwoWay}"
MinWidth="130"
Margin="0,0,0,10"
/>
Your viewmodel property must be System.Windows.Media.Color, not System.Drawing.Color:
public static System.Windows.Media.Color TextColour { get; set; }
And lastly, you must implement INotifyPropertyChanged properly in your viewmodel and raise PropertyChanged when the value of TextColour changes or else those other bindings, on the TextBox, will have no way of knowing that anything changed.
Related
I'm binding a List<string> to my ListBox in WPF using MVVM
At the moment I have
<ListBox ItemsSource="{Binding FileContents}"></ListBox>
File Contents in my ViewModel is simply
public List<string> FileContents {get;set;}
And the FileContents values are set in the constructor of the ViewModel, as such there is no need to worry about INotifyProperty
Everything works fine so far. I can see the list displayed in my ListBox as desired.
Now I need to provide a template! This is where it goes wrong
<ListBox ItemsSource="{Binding FileContents}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This is where it all goes wrong! My understanding is that I only need to do <TextBox Text = "{Binding}" because the ListBox is already bound to the List<string> property (called FileContents)
However, when I run the above Visual Studio gives me
The application is in break mode
If I update the code to
<TextBox Text = "Some String Value"
then it works fine
I don't understand what I've done wrong.
Set the Mode of the Binding to OneWay:
<TextBox Text="{Binding Path=., Mode=OneWay}" />
The default binding mode for the Text property of a TextBox is TwoWay but this won't work when you bind to a string in a List<string>.
Binding to a string directly is only possible one way. This means you are only able to bind read only like
<TextBox Text="{Binding Mode=OneWay}"/>
or
<TextBox Text="{Binding .}"/>
The reason is simple: Changing the string means you are removing and adding an item to your list. This is simply not possible by changing the string in a TextBox.
A solution is to wrap the content in a class like
public class FileContent
{
public string Content { get; set; }
}
and bind to a list of List<FileContent> by using <TextBox Text="{Binding Content}"/> as template.
I'm trying to extend the app from a WPF MVVM tutorial as an exercise. I've found no solution on the net for this specific problem I'm facing here.
I have a ViewModel with an ObservableCollection called "StudentsToAdd". This collection is bound to an ItemsControl. Outside the ItemsControl I have a Button with a binding to the "AddCommand" command in the ViewModel. The relevant extract form my XAML looks as follows:
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Button Content="Add" Command="{Binding AddCommand}" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75"/>
<Button Content="+" Command="{Binding AddToAddListCommand}" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="3,0,3,0" Margin="50,0,0,0"/>
<Button Content="-" Command="{Binding RemoveFromAddListCommand}" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="5,0,5,0" Margin="5,0,0,0"/>
</StackPanel>
<ItemsControl x:Name="AddList" ItemsSource="{Binding Path=StudentsToAdd}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Path=FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100" Margin="0 5 3 5">
<TextBox.InputBindings>
<KeyBinding Command="{Binding ElementName=AddList, Path=DataContext.AddCommand}" Key="Return"/>
</TextBox.InputBindings>
</TextBox>
<TextBox Text="{Binding Path=LastName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100" Margin="0 5 3 5">
<TextBox.InputBindings>
<KeyBinding Command="{Binding ElementName=AddList, Path=DataContext.AddCommand}" Key="Return"/>
</TextBox.InputBindings>
</TextBox>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
The + and - buttons will add or remove students from the StudentsToAdd collection. The "AddCommand" moves all entries from StudentsToAdd to another collection called "Students" when executed.
Now what I can't get to work is this: whenever a Student in StudentsToAdd is modified (after any keystroke: UpdateSourceTrigger=PropertyChanged). I want the Add Button to evaluate the CanExecute of AddCommand in the ViewModel so its IsEnabled property is automatically set accordingly. The command methods in the ViewModel currently look as follows:
private void OnAdd()
{
foreach (Student s in StudentsToAdd)
{
Students.Add(s);
}
StudentsToAdd.Clear();
StudentsToAdd.Add(new Student { FirstName = string.Empty, LastName = string.Empty });
}
private bool CanAdd()
{
if (StudentsToAdd != null && StudentsToAdd.Count > 0)
{
return StudentsToAdd.All(x => !string.IsNullOrWhiteSpace(x.FirstName) && !string.IsNullOrWhiteSpace(x.LastName));
}
return false;
}
Does anybody know how I can achieve this without coupling parts of the MVVM?
Does anybody know how I can achieve this without coupling parts of the MVVM?
There's not much context in your question. But, it seems that you are asking how to accomplish this entirely within the view model/model layer, without involving the view layer. At least, that's what you should be asking.
If so, it should be relatively simple, assuming the code you didn't show is written reasonably. That is, since your CanAdd() method depends on property values of the Student objects, you'll need to subscribe to the Student.PropertyChanged event, and raise the ICommand.CanExecuteChanged event any time any of the Student objects' PropertyChanged event is raised.
For what it's worth, I would also encapsulate the "can be added" logic in the Student class, rather than the ViewModel class. Expose that state as a single property that the ViewModel class can check. This will address a couple of things:
Your Student class seems like the more logical place to put code that determines whether the class is ready to be added to a list of Students, and
The ViewModel class can check to make sure it's that property that is changing, so it doesn't bother to go to all the work to check all the other Student objects every time any Student property changes, and each Student object will effectively be caching the "can be added" value, so that that work to check all the other Student objects is a simple property retrieval, instead of having to re-evaluate the state every single time.
I assume you already understand how to raise the ICommand.CanExecuteChanged event, but if not, here are a couple of posts that should help you with that:
CanExecuteChanged event of ICommand
ICommand CanExecuteChanged not updating
(You'll see that there are two basic strategies: implement something in the ICommand object that will explicitly raise the ICommand.CanExecuteChanged event, or call InputManager.InvalidateRequerySuggested() to force the Input Manager to call all the CanExecute() methods it knows about. IMHO, the latter is a pretty heavy-weight and less-desirable approach, hence my suggestion to use the ICommand.CanExecuteChanged event.)
I'm new to MVVM and I'm working on a WP8 app and I'd like to be able to set the visibility of buttons and a textblock based on when one of those buttons were tapped. Here's my View to try and explain my problem a bit better; (http://i.imgur.com/JvrxBkh.png - can't post an image on this reputation) .
When the user taps the "Going to sleep" button, I'd like the counter textblock and the "I'm awake" button to be visible with the "Going to sleep" button to be collapsed. It'll then work the other way once the "I'm awake" button is pressed, etc. If I wasn't using MVVM I'd just set the Visibility value inside the button event, but I'm stuck on how to do this when using the MVVM pattern.
I've looked around and come across a solution using a converter such as using a BooleanToVisibilityConverter class and a bool property and then setting the visibility by binding to the bool value from the ViewModel and setting the converter value for the visibility to the StaticResource BooleanToVisibilityConverter. But it just doesn't work for me the way I want. Then my counter textblock has a bind already from the ViewModel so would I need some kind of multi-binding for this textblock?
Hopefully I've explained myself OK. It seems like it should be a simple task that maybe I'm just over thinking or something.
EDIT With some code snippets
The View components that I was referring to;
<BooleanToVisibilityConverter x:Key="boolToVis" />
<TextBlock
Grid.Row="2"
Text="{Binding Counter}"
FontSize="50"
TextWrapping="Wrap"
Foreground="White"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility="{Binding VisibilityControl, Converter={StaticResource boolToVis}}"/>
<Button
Grid.Row="3"
Width="230"
Height="70"
Content="I'm awake"
BorderThickness="0"
Background="Gray"
Margin="0,20,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding AwakeButtonCommand}"
Visibility="{Binding VisibilityControl, Converter={StaticResource boolToVis}}""/>
<Button
Grid.Row="3"
Width="230"
Height="70"
Content="Going to sleep"
BorderThickness="0"
Background="Gray"
Margin="0,20,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Command="{Binding SleepButtonCommand}"
Visibility="{Binding VisibilityControl, Converter={StaticResource boolToVis}}"/>
Then in the ViewModel VisibilityControl is just;
private bool _visibilityControl;
public bool VisibilityControl
{
if (_visibilityControl != value)
_visibilityControl = value;
OnPropertyChanged("VisibilityControl");
}
And I have the two buttons such as (I'll just post one up);
public ICommand AwakeButtonCommand
{
get
{
return _awakeButtonCommand
?? (_awakeButtonCommand = new Resources.ActionCommand(() =>
{
VisibilityControl = true;
}));
}
}
It doesn't work, obviously. I think what's throwing me is because I want several things changed when one button is pressed, etc. It's throwing me off.
I've not done any Windows Phone development but here's one way of doing it in WPF that might be applicable to WP also.
First, your ViewModel would have a couple of Boolean properties indicating which state is active (one would be a mirror of the other):
public bool IsAwake
{
get
{
return _isAwake;
}
set
{
if (_isAwake != value)
{
_isAwake = value;
// raise PropertyChanged event for *both* IsAwake and IsAsleep
}
}
}
bool _isAwake;
public bool IsAsleep
{
get
{
return !_isAwake;
}
}
Then your View would contain both parts of the UI (asleep & awake) but would switch between the two parts by binding their Visibility property to these Boolean properties of your ViewModel:
<StackPanel>
<StackPanel x:Name="AwakePart"
Visibility="{Binding IsAwake, Converter={StaticResource btvc}}">
... "Going to sleep" button here ...
</StackPanel>
<StackPanel x:Name="AsleepPart"
Visibility="{Binding IsAsleep, Converter={StaticResource btvc}}">
... Elapsed time text block and "I'm awake" button here ...
</StackPanel>
</StackPanel>
You will also need a BooleanToVisibilityConverter instance somewhere in your XAML resources:
<... .Resources>
<BooleanToVisibilityConverter x:Key="btvc" />
</... .Resources>
I've used two Boolean properties in this example as it makes the XAML a little easier, however you could also use a DataTrigger -- assuming they have those in Windows Phone -- in which case you would only need one Boolean property. You would then write a trigger to toggle the Visibility properties of the two parts:
<DataTrigger Binding="{Binding IsAwake}" Value="True">
<Setter TargetName="AwakePart" Property="Visibility" Value="Visible" />
<Setter TargetName="AsleepPart" Property="Visibility" Value="Hidden" />
</DataTrigger>
For this to work you would need to explicitly set the "AwakePart" visibility to Hidden in the XAML to start with and ensure that in your ViewModel the IsAwake property is false by default. You would also need to remove the bindings on the visibility properties (as these would now be set via the trigger).
I am making the UIs for entering master data for various business entities (customer, etc). I run into this need to group together a TextBlock and a TextBox together frequently. i.e.
<Label Content="Civil Status:" Grid.Column="0" Grid.Row="1" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />
<TextBox Grid.Column="1" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="3" Name="civilStatusTextBox" Text="{Binding Path=CivilStatus, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" VerticalAlignment="Center" Width="120" />
<Label Content="Company:" Grid.Column="0" Grid.Row="2" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />
<TextBox Grid.Column="1" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="3" Name="companyTextBox" Text="{Binding Path=Company, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" VerticalAlignment="Center" Width="120" />
Is there any way to do this with less typing? i.e.
<custom:LabeledTextBox Label="Civil Status:" Text="{Binding Path=CivilStatus, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true}" ... />
Or possibly, any libs which offer something like this?
EDIT : Forget the container Grid for a moment and assume it is a StackPanel.
Here's a step-by-step solution that I managed to put together. To set the stage, I'm going to assume we've got a very simple UserControl that has the following XAML content.
<UserControl x:Class="WpfApplication2.UserControl1" [ ... auto gen code removed ... ] >
<TextBox MinWidth="50" x:Name="TBox" />
</UserControl>
From an XAML that uses our UserControl we'd essentially want to set a data binding for the Text property on TBox. Idealy we could use a plain syntax like:
<local:UserControl1 TBox.Text="{Binding ...}" />
unfortunately I don't know any XAML syntax that would allow targeting an sub-element's property, so the next best thing would be to introduce a "staging" property in the UserControl itself and bind through that.
Since Binding only works for Dependency properties, the property we'll introduce needs to be a DependencyProperty. We'll also bind the Text property of TBox straight to our DependencyProperty from code.
The code-behind for the UserControl looks like this:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
// Set binding from code
this.TBox.DataContext = this;
this.TBox.SetBinding(TextBox.TextProperty, new Binding { Path = new PropertyPath("TBValue"), Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged });
}
public static readonly DependencyProperty TBValueProperty = DependencyProperty.Register("TBValue", typeof(string), typeof(UserControl1));
public string TBValue
{
get { return this.GetValue(TBValueProperty) as string; }
set { this.SetValue(TBValueProperty, value); }
}
}
}
With this in place we can use the UserControl like this, binding to the TBValue property:
<local:UserControl1 TBValue="{Binding Path=Test, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
What you want to achieve (Master-Detail-Views) is actually well supported by Visual Studio out of the box. Open the following Menu structure : Project -> Add Data Source, then choose data source type Object. In the following, select the classes that you want to generate input fields for and finish the wizard.
Then, if not already open, open up your Data Sources tool window (Shift+Alt+D). You should see a DataSource of the type you just generated. To get a labelled field for each property of the object, open the source dropdown and click Details.
Note that the properties have such dropdowns as well, so that you can choose how you want to edit them (ComboBox, TextBox, Custom, no editor,...).
Now just drag the DataSource onto your window. You will get a Grid that's filled with all the labels and editors you desired. DataBinding and validation is also supported right away, so all you will have to do is set the generated Grid's DataContext.
Hope this saves you some work.
P.S. The screenshots are made in my german VS instance, still I thought they might help you identify the right dialogues / windows.
Assuming I have a simple class that represents a staff member
class Staff
{
public string FirstName { get; set; }
public string FamilyName { get; set; }
public int SecondsAlive { get; set; }
}
and I have a DataTemplate for staff
<DataTemplate DataType={x:Type Staff}>
<StackPanel Orientation="Horizontal">
<TextBlock Text={Binding FirstName}/>
<TextBlock Text=" ">
<TextBlock Text={Binding FamilyName}/>
<StackPanel.ToolTip>
<TextBlock Text={Binding SecondsAlive}/>
</StackPanel.ToolTip>
</StackPanel>
</DataTemplate>
I then show a whole bunch of staff in a ListBox
myListBox.ItemsSource = GetAllStaff();
Pretty standard stuff. The problem I have is that the tooltip which shows the number of seconds that someone has been alive does not get updated. When you first mouse over a staff member then it works fine but from then on it keeps that value for ever. I could implement INotifyPropertyChanged to get around this but it seems like overkill to do this for every staff member whenever SecondsAlive changes. Say I have 400 staff in the list then I have to raise 400 events even though the user might never look at another tooltip. What I would like is to make the tooltip request the SecondsAlive property ever time it is shown. Is that possible?
Please note that this is just an example and I don't need to know how many seconds my staff have been alive :-) But I have the same issue that I need to raise an even around 400 times just for a tooltip which someone probably won't look at.
OMG!!! I have finally found the solution to this problem!!! This has been bugging me for months. I'm not surprised no one answered this because the code I typed out at the top actually DIDN'T show the problem I was trying to reproduce, in fact it showed the solution. The answer is that if you define your tooltip like this
<StackPanel.ToolTip>
<TextBlock Text="{Binding SecondsAlive}"/>
</StackPanel.ToolTip>
Then everything works just fine and dandy and there is no need to raise a propertyChanged event on "SecondsAlive". The framework will call the SecondsAlive property every time the tooltip is shown. The problem comes when you define your tooltip like this:
<StackPanel.ToolTip>
<ToolTip>
<TextBlock Text="{Binding SecondsAlive}"/>
</ToolTip>
</StackPanel.ToolTip>
Having the extra tooltip tag in there makes sense, surely you need to create a tooltip object to assign it to the tooltip property but this is incorrect. What you are assigning to the tooltip property is actually the content of the tooltip. I was assuming you needed to give it controls such as textblock and image to display but you can pass in anything and it will display the content just like a content control. Seeing it inherits from content control this makes sense :-) It all seems obvious once you know :-)
Thanks everyone for looking at this.
PS. I found an additional problem in that the next logical step in simplifying code is to just assign text straight to the tooltip like this (assuming your tooltip is plain text):
<TextBlock Text="{Binding Path=StaffName}" ToolTip="{Binding Path=StaffToolTip}"/>
This also causes the original problem I was having. This makes sense because the results of the property StaffToolTip get assigned to the tooltip property and never get called again. However, it doesn't quite make sense why then assigning a TextBlock to the tooltip property actually solves the problem.
Although this is an old question.
In this case:
<StackPanel.ToolTip>
<ToolTip>
<TextBlock Text="{Binding SecondsAlive}"/>
</ToolTip>
</StackPanel.ToolTip>
The ToolTip control is hosted by an isolate HWND (a.k.a, a native window). It should have its own DataContext, the correct binding expresion should be like:
<StackPanel.ToolTip>
<ToolTip
DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<TextBlock Text="{Binding SecondsAlive}"/>
</ToolTip>
</StackPanel.ToolTip>
In this particular case there is a cool trick you can use
Seconds Alive Now = Seconds Alive originally + Elapsed Time
You can bind to the Elapsed Time property and specify a converter that adds the initial value to it. That way you only need to raise 1 event and the tooltips would all be updated.
Edit: You can add the ElapsedTime property (with INotifyPropertyChanged) to many places -- one logical place could be to the collection that is storing your Staff objects
Edit: You would also need to bind each tooltip to the shared ElapsedTime property rather than the SecondsAlive property
It's worth noting that the ToolTip appears to check your object that it's bound to for equality before reloading itself with the new data.
In my case I did an override of
public override bool Equals(object obj)
and
public override int GetHashCode()
on a class with properties
public class MultipleNameObject { string Name, string[] OtherNames};
Unfortunatley I only did a string.Compare() on the Name property of the MultipleNameObject for equality purposes. The tool tip was supposed to display OtherNames in a ItemsControl, but was not updating if Name was equal on the previous MultipleNameObject that the mouse hovered been over on the grid, even if the OtherNames were different.
[edit] Running with debug enabled confirms that the GetHashCode() override of my object was being used by the ToolTip to decide whether to grab the new data back. Fixing that to take the string[] OtherNames into account fixed the problem.
This example shows how to add a tooltip to a grid that recalculates the tooltip on demand, when the user hovers over a cell in the grid.
This is useful if you have a huge grid with 10,000 items, and you want to update the grid continuously - but you don't want to update the tooltips continuously, as this is time consuming.
This example is for Infragistics, but the principle applies equally to other fine libraries such as DevExpress.
Xaml
<ig:TemplateColumn Key="ColumnKey">
<ig:TemplateColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="Header"></TextBlock>
</DataTemplate>
</ig:TemplateColumn.HeaderTemplate>
<ig:TemplateColumn.ItemTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel.ToolTip>
<TextBlock Text="{Binding ToolTip}"/>
</StackPanel.ToolTip>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter" >
<i:InvokeCommandAction Command="{Binding ToolTipHoverRefreshCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</StackPanel>
</DataTemplate>
</ig:TemplateColumn.ItemTemplate>
</ig:TemplateColumn>
ViewModel
public ICommand ToolTipHoverRefreshCommand => new DelegateCommand(() =>
{
this.OnPropertyChanged(this.ToolTip);
});
public string ToolTip
{
get
{
return DateTime.Now.ToString();
}
set
{
this.OnPropertyChanged(nameof(this.ToolTip));
}
}
I was having the same problem of it not updating. I found the solution to be adding the controls to a ToolTip template:
<DataTemplate DataType={x:Type Staff}>
<StackPanel Orientation="Horizontal">
<TextBlock Text={Binding FirstName}/>
<TextBlock Text=" ">
<TextBlock Text={Binding FamilyName}/>
<StackPanel.ToolTip>
<Tooltip>
<ToolTip.Template>
<ControlTemlate>
<TextBlock Text={Binding SecondsAlive}/>
</ControlTemplate>
</ToolTip.Template>
</Tooltip>
</StackPanel.ToolTip>
</StackPanel>
</DataTemplate>
I don't quite understand why this is needed or why this makes it behave differently but it fixed the problem.