Combining three slider values into one Texblock Text / Label Content? - c#

I'm currently working on a User Interface in which the user can select three values via three separate sliders. I've got three textblocks in front of the sliders to indicate the current value of the specific slider:
In the .xaml:
<Label Content="Sample Selection" FontSize="16" FontStyle="Italic" FontWeight="Bold" HorizontalAlignment="Left" Margin="0,-30,0,0" VerticalAlignment="Top"/>
<Label Content="Patient Samples (max 64)" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" FontStyle="Italic"/>
<Slider x:Name="SampleAmountSlider" HorizontalAlignment="Left" Margin="181,14,0,0" VerticalAlignment="Top" Cursor="Hand" Width="160" Maximum="64" ValueChanged="SampleAmountSlider_ValueChanged" IsSnapToTickEnabled="True"/>
<TextBlock x:Name="SampleSliderValue" HorizontalAlignment="Left" Margin="165,16,0,0" TextWrapping="Wrap" Text="0" VerticalAlignment="Top"/>
<Label Content="Calibrators (max 7)" HorizontalAlignment="Left" Margin="10,36,0,0" VerticalAlignment="Top" FontStyle="Italic"/>
<Slider x:Name="CalAmountSlider" HorizontalAlignment="Left" Margin="181,40,0,0" VerticalAlignment="Top" Cursor="Hand" Width="160" Maximum="7" ValueChanged="CalAmountSlider_ValueChanged" IsSnapToTickEnabled="True"/>
<TextBlock x:Name="CalSliderValue" HorizontalAlignment="Left" Margin="165,42,0,0" TextWrapping="Wrap" Text="0" VerticalAlignment="Top"/>
<Label Content="Control Samples (max 4)" HorizontalAlignment="Left" Margin="10,62,0,0" VerticalAlignment="Top" FontStyle="Italic"/>
<Slider x:Name="ControlAmountSlider" HorizontalAlignment="Left" Margin="181,66,0,0" VerticalAlignment="Top" Cursor="Hand" Width="160" Maximum="4" ValueChanged="ControlAmountSlider_ValueChanged" IsSnapToTickEnabled="True"/>
<TextBlock x:Name="ControlSliderValue" HorizontalAlignment="Left" Margin="165,68,0,0" TextWrapping="Wrap" Text="0" VerticalAlignment="Top"/>
<Label Content="Total Sample Preparations Selected:" HorizontalAlignment="Left" Margin="10,105,0,0" VerticalAlignment="Top" FontWeight="Bold" FontStyle="Italic"/>
<TextBlock x:Name="TotalPrepValue" HorizontalAlignment="Left" Margin="225,110,0,0" FontWeight="Bold" FontStyle="Italic" Text="0" VerticalAlignment="Top"/>
In .xaml.cs:
private void SampleAmountSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
SampleSliderValue.Text = Math.Round(e.NewValue, 0).ToString();
}
private void CalAmountSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
CalSliderValue.Text = Math.Round(e.NewValue, 0).ToString();
}
private void ControlAmountSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
ControlSliderValue.Text = Math.Round(e.NewValue, 0).ToString();
}
What I want is the last text block (named TotalPrepValue) to contain a sum of the amount of patient samples, amount of calibrators and amount of control samples.
I'm not sure if I'm asking a lot or things are unclear (if so, please let me know. I'll answer as fast as possible).
The thing is that I'm a very inexperienced programmer, yet willing to learn!
Thank you in advance for your help!

Personally, this is what I would do. However if you like it than please go also upvote #JerryNixon 's answer here since it's basically just a re-factoring to use sliders instead of his example and he deserves more praise. Normally I'd have just pointed you to it directly but I know how it is when you're just getting started with something and a clearer PoC can be more useful.
Anyway, here ya go, a pretty picture to start...
The XAML;
<Window.Resources>
<local:SumConverter x:Key="MySumConverter" />
</Window.Resources>
<Grid>
<StackPanel VerticalAlignment="Center">
<StackPanel.Resources>
<Style TargetType="Slider">
<Setter Property="Margin" Value="10"/>
<Setter Property="Width" Value="200"/>
<Setter Property="Minimum" Value="0"/>
<Setter Property="Maximum" Value="100"/>
</Style>
</StackPanel.Resources>
<Slider x:Name="Slider1"></Slider>
<Slider x:Name="Slider2"></Slider>
<Slider x:Name="Slider3"></Slider>
<TextBlock HorizontalAlignment="Center" TextAlignment="Center">
<Run Text="{Binding Value, ElementName=Slider1}"/>
<LineBreak/><LineBreak/>
<Run Text="{Binding Value, ElementName=Slider2}"/>
<LineBreak/><LineBreak/>
<Run Text="{Binding Value, ElementName=Slider3}"/>
<LineBreak/>
<Run Text="______________________"/>
<LineBreak/><LineBreak/>
<Run>
<Run.Text>
<MultiBinding Converter="{StaticResource MySumConverter}"
StringFormat="{}{0:C}"
FallbackValue="Error" TargetNullValue="Null">
<Binding Path="Value" ElementName="Slider1"/>
<Binding Path="Value" ElementName="Slider2"/>
<Binding Path="Value" ElementName="Slider3"/>
</MultiBinding>
</Run.Text>
</Run>
</TextBlock>
</StackPanel>
</Grid>
The Converter;
public class SumConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
double _Sum = 0;
if (values == null)
return _Sum;
foreach (var item in values)
{
double _Value;
if (double.TryParse(item.ToString(), out _Value))
_Sum += _Value;
}
return _Sum;
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Hope this helps, cheers.

You can, as others have suggested, use IMultiValueConverter in this scenario. But it's my opinion that, while a useful tool in other scenarios, that's the wrong tool for the job here. The reason being that in this case, it would be used to perpetuate the inappropriate use of UI elements as a place to store your non-UI data.
You will be much better served, while writing WPF programs, if you commit yourself to following the MVVM-style of programming WPF was intended to be used with. The term "MVVM" means literally "model, view, view-model". From that point of view, there will always be special-purpose "adapter" types between the model and the view. But it's been my experience that the important part of the MVVM paradigm is to be strict about keeping view logic separate from model logic, and this can often be done without the extra layer of the "view model" types. This puts MVVM in the same set of tools as MVC ("model, view, controller") and MVP ("model, view, presenter").
The key to all of these is that you have some business logic which is represented in model data structures, implemented by types that provide some form of value-changed notification (in WPF, the primary mechanism here is INotifyPropertyChanged), and then also view logic which is represented completely separately (in WPF, the view is mostly, and in many cases entirely, declared in XAML).
In your example, this means we'd want a model data structure that represents the data you are interested in: the sample, calibrator, control, and total prep counts. The last one being simply the sum of the first three. As long as we have a class that can keep track of these, and correctly update the summed value when any of the other three change, we can bind this directly to a view declared in XAML, without the use of any C# code-behind at all.
For example:
class ViewModel : INotifyPropertyChanged
{
private int _sampleCount;
public int SampleCount
{
get { return _sampleCount; }
set { _UpdateField(ref _sampleCount, value, OnCountChanged); }
}
private int _calibratorCount;
public int CalibratorCount
{
get { return _calibratorCount; }
set { _UpdateField(ref _calibratorCount, value, OnCountChanged); }
}
private int _controlCount;
public int ControlCount
{
get { return _controlCount; }
set { _UpdateField(ref _controlCount, value, OnCountChanged); }
}
private int _totalPrepCount;
public int TotalPrepCount
{
get { return _totalPrepCount; }
set { _UpdateField(ref _totalPrepCount, value); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnCountChanged(int previousValue)
{
TotalPrepCount = SampleCount + CalibratorCount + ControlCount;
}
protected void _UpdateField<T>(ref T field, T newValue,
Action<T> onChangedCallback = null,
[CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, newValue))
{
return;
}
T oldValue = field;
field = newValue;
onChangedCallback?.Invoke(oldValue);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Notes:
The above class has four properties, one property for each of the values we want to keep track of.
Three of the properties are just simple value containers. There is a callback method that is called whenever any of those are modified, and in that method the code simply sets the fourth property to the sum of the three.
The INotifyPropertyChanged interface has just a single member, the PropertyChanged event. You will find when dealing with MVVM-style code, it's helpful to have a base class that actually implements this event, and a helper method like the _UpdateField() method shown above, which property setters can call to handle the repetitive logic needed for each such property. In the example above, I've combined all of this logic into a single class for the sake of simplifying the example, but you'll probably want to keep a suitable base class around (I and many other people have configured snippets in Visual Studio to easily insert this boilerplate code into a project).
With a view model so-defined, the XAML is simplified to look something like this:
<Window x:Class="TestSO45170241SliderExample.MainWindow"
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:l="clr-namespace:TestSO45170241SliderExample"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<l:ViewModel/>
</Window.DataContext>
<Grid>
<Label Content="Sample Selection" FontSize="16" FontStyle="Italic" FontWeight="Bold"
HorizontalAlignment="Left" Margin="0,-30,0,0" VerticalAlignment="Top"/>
<Label Content="Patient Samples (max 64)" HorizontalAlignment="Left" Margin="10,10,0,0"
VerticalAlignment="Top" FontStyle="Italic"/>
<Slider HorizontalAlignment="Left" Margin="181,14,0,0"
VerticalAlignment="Top" Cursor="Hand" Width="160" Maximum="64"
Value="{Binding SampleCount}" IsSnapToTickEnabled="True"/>
<TextBlock HorizontalAlignment="Left" Margin="165,16,0,0"
TextWrapping="Wrap" Text="{Binding SampleCount}" VerticalAlignment="Top"/>
<Label Content="Calibrators (max 7)" HorizontalAlignment="Left" Margin="10,36,0,0"
VerticalAlignment="Top" FontStyle="Italic"/>
<Slider HorizontalAlignment="Left" Margin="181,40,0,0"
VerticalAlignment="Top" Cursor="Hand" Width="160" Maximum="7"
Value="{Binding CalibratorCount}" IsSnapToTickEnabled="True"/>
<TextBlock HorizontalAlignment="Left" Margin="165,42,0,0"
TextWrapping="Wrap" Text="{Binding CalibratorCount}" VerticalAlignment="Top"/>
<Label Content="Control Samples (max 4)" HorizontalAlignment="Left" Margin="10,62,0,0"
VerticalAlignment="Top" FontStyle="Italic"/>
<Slider HorizontalAlignment="Left" Margin="181,66,0,0"
VerticalAlignment="Top" Cursor="Hand" Width="160" Maximum="4"
Value="{Binding ControlCount}" IsSnapToTickEnabled="True"/>
<TextBlock HorizontalAlignment="Left" Margin="165,68,0,0"
TextWrapping="Wrap" Text="{Binding ControlCount}" VerticalAlignment="Top"/>
<Label Content="Total Sample Preparations Selected:" HorizontalAlignment="Left"
Margin="10,105,0,0" VerticalAlignment="Top" FontWeight="Bold" FontStyle="Italic"/>
<TextBlock HorizontalAlignment="Left" Margin="225,110,0,0"
FontWeight="Bold" FontStyle="Italic" Text="{Binding TotalPrepCount}"
VerticalAlignment="Top"/>
</Grid>
</Window>
(Aside: other than changing to support the MVVM approach, I did not modify your basic UI declarations at all. I do agree that another thing you'll want to start gaining familiarity with is how to take advantage of WPF's various layout containers and element styling features. But I think that introducing those here would just confuse matters. By keeping your original UI mostly intact, you can focus on just those things that are different from what you originally had, helping you understand better the data binding aspect without distraction.)
In this implementation, there is no code whatsoever added to the MainWindow.xaml.cs file. All that's in that file is the default call to InitializeComponent() in the constructor, provided by Visual Studio's template for WPF projects.
In the XAML, on the other hand, I've replaced the event handler subscriptions with bindings straight to the Slider.Value properties. Note also that the TextBlock.Text properties are also bound to the same properties. In this way, WPF does all the heavy lifting of storing slider values in a business-logic-only data structure, as well as of then redisplaying those values in text fields in the view. You'll note that WPF even handles conversion between the various data types: the view model stores int values, but the sliders use double and the text blocks of course use string.
And of course, the TotalPrepCount field is bound to the TextBlock.Text property of interest for display as well.
Finally, I'll note that even in your simple example, you have additional places where one might want to apply this data binding approach. In particular, your sliders each have maximum values, which are hard-coded into the view. The point of MVVM is for the view to not have to encapsulate any knowledge about the business logic. This would include not having to know the full range of values permissible (*).
So, your view model could also have e.g. a MaxSampleCount property, which is bound both to the Slider.Maximum property, and to the Label.Content property. In the latter case, you can use the Binding.StringFormat property to have the value incorporated into text as appropriate. For example:
<Label Content="{Binding MaxSampleCount, StringFormat=Patient Samples (max {0})" ... />
I will readily admit that when I first started trying to use WPF, after years and years of using UI APIs like the native Win32 controls, MFC, Windows Forms, Java's APIs (Swing, AWT, SWT), and even Mac OS's Cocoa framework (which uses a form of data binding, but not the declarative UI like XAML), I struggled to change my thinking away from the procedural approach used in all those other APIs to get used to the mixed declarative and procedural approach used with WPF.
But I was trying to learn it strictly from the documentation provided by MSDN, which is densely written at best, most often just plain difficult to follow, and in many cases, completely useless. If someone had just shown me an example like what I've shown above (and they did exist, even back then…I just didn't know where they were), I would have seen back then just how easy the basic MVVM approach is, and how much more quickly one can write a WPF program if one follows that approach.
I hope the above helps you get on track for productive use of WPF, and shows you the basics in an easy-to-understand way.

Related

Computing and viewing column outside model using MVVM

I need to solve following problem using C# MVVM. I'm using following model.
And one of my UserControls got following ListBox template.
<ListBox ItemsSource="{Binding OrdersListViewViewModel.AllItems, Source={StaticResource Locator}}" SelectedItem="{Binding OrdersListViewViewModel.SelectedItem, Source={StaticResource Locator}}" Background="White">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" Margin="0 0 0 0" Height="Auto" Width="Auto" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,7,0,6" HorizontalAlignment="Left" Orientation="Horizontal">
<Image Width="25" Height="25" Margin="5 2 0 0" Source="{Binding OrdersListViewViewModel.OrderDeliveryStateImage, Mode=OneWay, Source={StaticResource Locator}}"/>
<TextBlock Margin="25,5,25,5" Text="{Binding OrdersListViewViewModel.AllItems/Customer.CustomerName, FallbackValue=N/A, Mode=OneWay, Source={StaticResource Locator}}" FontSize="20"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The list is showing all orders (customer names) loaded from database. Image which is beside the TextBlock I want to fill in with a picture. If all orderitems under the order have been delivered (Delivered=1) it should use picture1, otherwise picture2.
So I'm binding ObservableCollection<Order>. Model is generated from .tt using entity framework (db first) so it's bad idea to place computing directly into Order.cs class because of possible db updates.
My first idea was to use MSSQL Computed column but I think that's not a good aproach (there can be a lot of situations like this one in a solution) so model would be huge and complicated.
Second idea was to use a converter but it should be used for a simple tasks, not for a computation logic (which this is).
Third idea was to change ObservableCollection<Order> to ObservableCollection<Tuple<string,Order>> and somehow bind it to a view but....you know, that's a bad idea.
So my question is simple. How can I solve this issue (where to place a computation logic of this purpose) using a MVVM best practice.
Thanks.
Well after a googling around I've decided to create this solution. Maybe it will help to someone.
First I've created partial class of Order entity in order to separate files so when entity framework will update the Order entity, it will not overwrite my customizations.
Then I've created custom property which determine if the Order has been delivered. So computation logic still stays at the model.
public partial class Order
{
public bool IsOrderDelivered
{
get
{
int orderDelivered = 1;
foreach (var orderItem in this.OrderItems)
{
orderDelivered = orderDelivered * orderItem.Delivered;
}
return orderDelivered == 1 ? true : false;
}
}
}
Then I've created converter which just converts boolean to text which is why it exists and it's used the right way.
public class OrderStatusToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value == true ? #"Skins\approved.png" : #"Skins\denied.png";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And created databinding on my new property in partial class together with converter.
<StackPanel Margin="0,7,0,6" HorizontalAlignment="Left" Orientation="Horizontal">
<Image Width="25" Height="25" Margin="5 2 0 0" Source="{Binding IsOrderDelivered, Converter={StaticResource OrderStatusToImageConverter}, Mode=OneWay}"/>
<TextBlock Margin="25,5,25,5" Text="{Binding Customer.CustomerName, FallbackValue=N/A, Mode=OneWay}" FontSize="20"/>
</StackPanel>
And finally the visual effect.

WPF Trigger Button CanExecute outside ItemsControl from within ItemsControl

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.)

Setting Textblock/Button Visibility on WP8 App using MVVM

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).

Generic UserControl Using Reflection and Generalization

in my project i've wrote some UserControl, i'm trying to reuse code as much as possible.
For each Model in my project i create an UserControl that shows the CRUD (a simple form ) and another that shows the List (listBox)
Is possible to Generalize that (for different types of Models with different attributes)?
Using Generalizatione and Reflection is possible to create something like a generic UserControl_Crud and generic UserControl_List?
So in my page.xaml i can use something like
<LUC:UserContro_Crud l x:Name="EmployeeUserControl_List" />
<LUC:UserContro_Crud l x:Name="CarsUserControl_List" />
<LUC:UserContro_Crud l x:Name="FruitUserControl_List" />
and in the code behind
EmployeeUserControl_List.MyProperty.ItemsSource = EmployeetList;
CarsUserControl_List.MyProperty.ItemsSource = CarstList;
//MyProperty give me back just the listbox from my UserControl
or
FruitUserControl_List.MyProperty.ItemsSource = EmployeetList;
to show 3 lists with differnt properties
#HighCore #Nate Diamond
In that way, for any kind of my Model, i have to make a particular template. For example CrudPersonTemplate `
<!-- my crud-->
<DataTemplate x:Key="MyCrud">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,20,0,20">
<TextBlock Text="Code"/>
<TextBox x:Name="edtCode" InputScope="Number" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,20,0,20">
<TextBlock Text="Name" />
<TextBox x:Name="edtName" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,20,0,20">
<TextBlock Text="Sex" />
<TextBox x:Name="edtSex" />
</StackPanel>
</StackPanel>
</DataTemplate>
than (afeter registered it in the app.xaml as a resource) i can use it in Page.xaml just typing someting like
<ContentControl x:Name="MyTemplate" Content="{Binding}" ContentTemplate="{StaticResource MyCrud}" />
... but it's hard to manage field like "edtName" from page.xaml.cs or not? i have to write more code than using UserControl to do that...
How can I manage as well as possible this field of my form?
where is the difference with UserControl? i have to bind manually the ContentTemplate...
I'm looking for a generic solution in page.xaml, in the "view" i just want to call a generic template/userControl, and "automatically" choose the relative layout according with the object type associated in page.xaml.cs (i hope to explain it better, i apologize for my ignorance)

How to group together Textblock and Textbox

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.

Categories