Howto create a ordered WPF multimonth index view? - wpf

Is it possible to declare a (linked) multimonth calendar Index View Control in pure Xaml? Disregarding the public interface of the resulting control.
What i intend to get would look like this (well, kind of):
<January> <February> <..>
1234556 12345678
78910 .. 91011...
Above you see two Calendars. If one of those calendars is switched forward or backward one month, the "neighbours" also have to switch state in that direction. Additionally the number of Visible Calendars should be determined by the current width and height of the control.
First, i was thinking of a WrapPanel. But what i try to find out is how to specifiy the itemssource (list of DateTime's) and the "link" between the calendars.
Any suggestions on how to solve this in best WPF manner?

You can link the calendars by binding the DisplayDate value to its adjacent Calendar control, and using a Value Converter to adjust the month. I've include a basic prototype to get you started.
Here is the XAML:
<Window x:Class="CalendarTriggers.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Converter="clr-namespace:CalendarTriggers"
Title="MainWindow"
Height="350" Width="525">
<Window.Resources>
<Converter:CalendarConverter x:Key="conv"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Calendar x:Name="Month1" Grid.Column="0" DisplayDate="2011/01/11" />
<Calendar x:Name="Month2" Grid.Column="1" DisplayDate="{Binding Path=DisplayDate, ElementName=Month1, Converter={StaticResource conv}}" />
<Calendar x:Name="Month3" Grid.Column="2" DisplayDate="{Binding Path=DisplayDate, ElementName=Month2, Converter={StaticResource conv}}"/>
</Grid>
</Window>
Here is the converter:
using System;
using System.Globalization;
using System.Windows.Data;
namespace CalendarTriggers
{
public class CalendarConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
DateTime input = DateTime.Now;
try
{
input = (DateTime)value;
if (input != null)
{
input = input.AddMonths(1);
}
}
catch (Exception)
{
}
return input;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
DateTime input = DateTime.Now;
try
{
input = (DateTime)value;
if (input != null)
{
input = input.AddMonths(-1);
}
}
catch (Exception)
{
}
return input;
}
}
}

Related

How to fill an Arc on an Arc other?

Currently, I have two Arcs but there are small borders of the wrong color (see black arrow) :
xaml:
<Window x:Class="WpfApplication9.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing">
<Grid Width="150" Height="150">
<ed:Arc
x:Name="BorderArc"
ArcThickness="15"
StrokeThickness="0"
ArcThicknessUnit="Pixel"
StartAngle="270"
EndAngle="45"
Fill="Red"
Stretch="None"
RenderTransformOrigin=".5 .5"
Width="150"
Height="150">
</ed:Arc>
<ed:Arc
x:Name="Border"
ArcThickness="15"
StrokeThickness="0"
ArcThicknessUnit="Pixel"
StartAngle="270"
EndAngle="30"
Fill="Gray"
Stretch="None"
RenderTransformOrigin=".5 .5"
Width="150"
Height="150">
</ed:Arc>
</Grid>
Please give me a solution for remove small red border (see black arrow)? Thanks for help me?
You could set the ArcThickness of the grey arc to 15.1, and see if that removes the red outline.
What happens if you give both arcs a white border of a single pixel?
Option 1: Modify the ViewModel
Could your ViewModel be modified to process the Arcs like this:
ArcViewModels = new List<SingleArcViewModel>();
for (var i = 0; i < Arcs.Count; i++)
{
if (i == 0)
{
ArcViewModels.Add(new SingleArcViewModel(Arcs[i].StartAngle, Arcs[i].EndAngle));
}
else
{
ArcViewModels.Add(new SingleArcViewModel(Arcs[i - 1].EndAngle, Arcs[i].EndAngle));
}
}
(I am assuming that the constructor takes StartAngle first, then EndAngle).
Where Arcs is your existing list of Arcs, and ArcViewModels is what you will bind your View to. It has the StartAngle of each arc set to the EndAngle of the previous one.
I've tried this in a simple application and it works OK, obviously you will have to make some modifications to your view model to process the arcs for binding to.
Option 2: Run the Arcs collection through an IValueConverter
You'll need to create a Converter class like this:
public class ArcCollectionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var arcsCollection = value as ArcViewModel[];
if (arcsCollection == null)
return value;
var result = new List<ArcViewModel>();
for (var i = 0; i < arcsCollection.Count(); i++)
{
if (i == 0)
{
result.Add(new SingleArcViewModel(arcsCollection[i].StartAngle, arcsCollection[i].EndAngle));
}
else
{
result.Add(new SingleArcViewModel(arcsCollection[i - 1].EndAngle, arcsCollection[i].EndAngle));
}
}
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then add the following to the references in your View:
xmlns:cc="clr-namespace:LocalExperiment.Models"
The resources:
<Window.Resources>
<cc:ArcCollectionConverter x:Key="ArcCollectionConverter" />
</Window.Resources>
And use the converter wherever you are binding to the arcs collection:
<ItemsControl ItemsSource="{Binding MyArcs, Converter={StaticResource ArcCollectionConverter}}"></ItemsControl>
You may need to modify some of the types to get it to work with your ViewModel, but hopefully this will get you started.
You have one arc on top of another. just modify them to not overlap:
<!-- I have ommited some properties -->
<ed:Arc StartAngle="30" EndAngle="45" Fill="Red" />
<ed:Arc StartAngle="275" EndAngle="30" Fill="Grey" />

How can i bind a border visibility to the visibility of containing children objects

I have this kind of code below, how can I bind the visibility of the Border to the visibility of all the labels?
Of course the number of rows and labels is not fixed.
<Border BorderBrush=Black
BorderThickness="1,1,1,1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label DataContext="{Binding MyObject[1]}"
Content="{Binding MyText}"
Visibility="{Binding IsVisible}"/>
<Label DataContext="{Binding MyObject[2]}"
Content="{Binding MyText}"
Visibility="{Binding IsVisible}"/>
[...]
</Grid>
</Border>
It depends on how you are changing the amount of rows and labels.
I assume that MyObject is a List<MyObject>. In that case what you can do is simply bind the list to the Visibility property with a Converter that loops through the objects checking if they are all invisible.
XAML:
Namespace:
xmlns:converters="clr-namespace:MyConverters"
Window:
<Window.Resources>
<converters:ObjectBorderVisibilityConverter
x:Key="MyObjectBorderVisibilityConverter"/>
</Window.Resources>
<Border BorderBrush=Black
BorderThickness="{Binding MyObject, Converter={StaticResource MyObjectBorderVisibilityConverter}">
[...]
Converters Code:
namespace MyConverters
{
public class ObjectBorderVisibilityConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Visibility v = Visibility.Hidden;
List<MyObject> myObjects = value as List<MyObject>;
foreach(Object myobject in myObjects)
{
if (myobject.IsVisible)
v = Visibility.Visible;
}
return v;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new InvalidOperationException("ObjectBorderVisibilityConvertercan only be used OneWay.");
}
}
}
Otherwise you are going to have to explain how you got the amount of rows and labels to be dynamic and we can work from there.
Hope this helps
u_u
EDIT
Well according to your comment you have a list of strings which contain the name of the object you want to display in each ListViewItem. I'm not going to ask why you are doing it this way, I assume you have a reason. I just wanna say have you tried Key Value pairs?
What I would do here is pass the grid itself as a parameter in the converter, and loop through its children using a LogicalTreeHelper inside the converter.
Revised Border:
<Window.Resources>
<converters:ObjectBorderVisibilityConverter
x:Key="MyObjectBorderVisibilityConverter"/>
</Window.Resources>
<Border BorderBrush=Black
BorderThickness="{Binding MyObject, Converter={StaticResource MyObjectBorderVisibilityConverter}", ConverterParameter={Binding ElementName=myGrid, BindsDirectlyToSource=True>
<Grid x:Name="myGrid">
[...]
Revised Converter
namespace MyConverters
{
public class ObjectBorderVisibilityConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Visibility v = Visibility.Hidden;
Grid myGrid = parameter as Grid;
List<MyObject> myObjects = value as List<MyObject>;
foreach (var child in LogicalTreeHelper.GetChildren(myGrid))
{
if(child.GetType() == typeof(System.Windows.Controls.Label)
if (((Label)child).Visibility = Visibility.Visible)
v = Visibility.Visible;
}
return v;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new InvalidOperationException("ObjectBorderVisibilityConvertercan only be used OneWay.");
}
}
}
I coded this all by hand so there's prolly a bunch of errors, but I hope you get the point.
u_u

Silverlight: How to mantain the same localization for all countries

I need to know how to format a given number (or date, or whatever)
always italian language, no matter in what country the client is...
Example:
<TextBlock Text={Binding Price, StringFormat=C2} />
must return "€ 1.520,45" in every country is executed.
even if Italian language is not installed in that machine.
How can i achieve that?
(possibly is better if i can do it application wide)
Thanks in advance.
You can set the UICulture and Culture of the Silverlight application explicitly to ensure that regardless of the user locale the UICulture and Culture would be fixed.
This can be achieved in two ways
1- Set in the object tag on the browser
<param name="uiculture" value="it-IT" />
<param name="culture" value="it-IT" />
2- Set the thread culture in the Application_Startup
Thread.CurrentThread.CurrentCulture = new CultureInfo("it-IT");
Thread.CurrentThread.CurrentUICulture = new CultureInfo("it-IT");
Update: The above does not seem to take effect when using StringFormat. Given this, I would revert to using a custom value converter. Below is a sample
MainPage.xaml
<UserControl x:Class="SLLocalizationTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SLLocalizationTest"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<local:DoubleToStringConverter x:Key="DoubleToStringConverter" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel>
<TextBlock Text="{Binding Price, Converter={StaticResource DoubleToStringConverter}, ConverterParameter=C2 }"/>
<TextBlock Text="{Binding Price, Converter={StaticResource DoubleToStringConverter} }"/>
</StackPanel>
</Grid>
</UserControl>
MainPage.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Globalization;
using System.Windows.Data;
namespace SLLocalizationTest
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
DataContext = this;
}
public double Price
{
get { return 12353.23; }
}
}
public class DoubleToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
if (value is double)
{
return ((double)value).ToString((string)parameter);
}
return value.ToString();
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

Binding WPF ComboBox to List<MyClass> where MyClass doesn't have properties, just methods

I want to bind a combo box to a list of Device, List. I use,
m_ctrlCB.DataContext = m_List;
m_ctrlCB.DisplayMemberPath = "ToString()";
m_ctrlCB.SelectedValuePath = "ToString()"; // do I even need this?
I don't have any properties in Device to bind to and it's not my class. However, they do override ToString to something that is suitable for displaying in the combobox (something like: "Class Device. Number 1".
However, what I wrote doesn't work. What I see in the combobox is blank items. My selectionChanged event does work AND e.AddedItems[0] really is a Device, so I'm close. How can I get something meaningful to display in the combox box.
I suppose I'd also be happy creating ComboBoxItems and adding them to the ComboBox if necessary. But if I go this route, how do I set the Display stuff and the actual object itself so I can get it when the user selects it from the combobox?
Bonus question. If instead of using ToString, I want to use GetDeviceNumber() and combine it with my own test so the user sees,
Device #1
Device #2
how would I do this?
thanks,
Dave
You don't have to set the DisplayMemberPath and the SelectedValuePath. Since your Device object overrides ToString(), it should display the correct string on its own.
EDIT:
To answer your "bonus question", one way to do this is to use an IValueConverter that calls the method you're interested in. The sample code below demonstrates this. I have here a combobox whose items are represented by a TextBlock (which shows the value for the ToString() method), as well as a Button (which shows the value for the GetDeviceNumber() method).
XAML:
<Window x:Class="StackOverflow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:StackOverflow"
Title="MainWindow" Height="350" Width="525"
x:Name="window">
<ComboBox x:Name="cb">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}"/>
<Button>
<Button.Content>
<Binding>
<Binding.Converter>
<local:DeviceValueConverter/>
</Binding.Converter>
</Binding>
</Button.Content>
</Button>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Window>
Code-Behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.cb.ItemsSource = new List<Device>()
{
new Device("Device1"),
new Device("Device2"),
new Device("Device3"),
};
}
}
public class Device
{
private string text;
public Device(string text)
{
this.text = text;
}
public string GetDeviceNumber() { return this.GetHashCode().ToString(); }
public override string ToString() { return this.text; }
}
public class DeviceValueConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is Device)
{
return (value as Device).GetDeviceNumber();
}
return string.Empty;
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new System.NotImplementedException();
}
#endregion
}
One way you could do it would be to create a wrapper class and provide the appropriate properties on it. For example:
class DeviceWrapper
{
private Device device;
public DeviceWrapper(Device device)
{
this.device = device;
}
public int DeviceNumber
{
return this.device.GetDeviceNumber();
}
// etc...
}
You should try to use ObjectDataProvider.
It will be something like this
...
<UserControl.Resources>
<ObjectDataProvider MethodName="GetValues"
ObjectType="{x:Type sys:Enum}"
x:Key="AlignmentValues">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="HorizontalAlignment" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</UserControl.Resources>
<Border Margin="10" BorderBrush="Aqua"
BorderThickness="3" Padding="8">
<StackPanel Width="300">
<TextBlock>bla-bla</TextBlock>
<ListBox Name="myComboBox" SelectedIndex="0" Margin="8"
ItemsSource="{Binding Source={StaticResource AlignmentValues}}"/>
<Button Content="Click Me!"
HorizontalAlignment="{Binding ElementName=myComboBox,
Path=SelectedItem}"/>
</StackPanel>
</Border>
...

Storing a radio button selection in the settings

I've been looking at this article but am having issues saving the enumerated value in the settings.
I have created the following enum
public enum FType
{
None,
Delimited,
FixedWidth,
XML
};
I have the radio button selection working nicely but I now want to store the selected option in the settings but there doesn't appear to be the ability to store an enumerated variable.
I assumed I could convert the enum to a string and then convert back but being a bit of a noob when it comes to WPF I'm not realy sure where to start.
Here is the code I've generated so far:
App.Xaml
<Application x:Class="Widget.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:properties="clr-namespace:Widget.Properties"
StartupUri="Window1.xaml"
Exit="Application_Exit">
<Application.Resources>
<properties:Settings x:Key="Settings" />
</Application.Resources>
</Application>
App.xaml.cs
public partial class App : Application
{
private void Application_Exit(object sender, ExitEventArgs e)
{
Widget.Properties.Settings.Default.Save();
}
}
Windows.xaml
<Window x:Class="Widget.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Widget"
Title="Window1" Height="85" Width="300">
<Window.Resources>
<local:EnumBooleanConverter x:Key="enumBooleanConverter"/>
</Window.Resources>
<Grid>
<StackPanel>
<RadioButton GroupName="FileType" Content="Delimited" IsChecked="{Binding Path=Default.FileType, Mode=TwoWay, Converter={StaticResource enumBooleanConverter}, ConverterParameter=Delimited}" />
<RadioButton GroupName="FileType" Content="Fixed Width" IsChecked="{Binding Path=Default.FileType, Mode=TwoWay, Converter={StaticResource enumBooleanConverter}, ConverterParameter=FixedWidth}"/>
<RadioButton GroupName="FileType" Content="XML" IsChecked="{Binding Path=Default.FileType, Mode=TwoWay, Converter={StaticResource enumBooleanConverter}, ConverterParameter=XML}"/>
</StackPanel>
</Grid>
</Window>
Converter.cs
public class EnumBooleanConverter : IValueConverter
{
public EnumBooleanConverter()
{
}
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string parameterString = parameter as string;
if (parameterString == null)
return DependencyProperty.UnsetValue;
if (Enum.IsDefined(value.GetType(), value) == false)
return DependencyProperty.UnsetValue;
object parameterValue = Enum.Parse(value.GetType(), parameterString);
return parameterValue.Equals(value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string parameterString = parameter as string;
if (parameterString == null)
return DependencyProperty.UnsetValue;
return Enum.Parse(targetType, parameterString);
}
#endregion
}
Your code looks just fine, except 2 problems that I think may be preventing you from storing settings:
I think you should specify a DataContext for your RadioButtons. Just modify your Window1 like this:
<StackPanel DataContext="{StaticResource Settings}">
<RadioButton GroupName=... />
<RadioButton GroupName=... />
<RadioButton GroupName=... />
</StackPanel>
(Note: If StaticResource doesn't work try using DynamicResource)
Secondly, from your post it seems that you are storing values as string in settings. Just change this and instead set datatype of FileType to Ftype. (If you don't know how 2 do this, tell me)
After doing these 2 changes you'll surely get this working! I hope ;)

Resources