开发者

WPF: Highlight year part of a date

开发者 https://www.devze.com 2023-03-24 15:46 出处:网络
I have an TextBlock showing a date using the standard short date format for the current culture. String.Format(culture, \"{0:d}\", someDate)

I have an TextBlock showing a date using the standard short date format for the current culture.

String.Format(culture, "{0:d}", someDate)

Now, the product manager wants the year hi开发者_JAVA技巧ghlighted in bold. At first, I thought it would be easy enough; have one run bound to the day/month-part, an a second to the year part.

<TextBlock>
  <Run Text="{Binding DayMonthPart}"/>
  <Run FontWeight="Bold" Text="{Binding YearPart}"/>
</TextBlock>

But that wont do, since different cultures have different ordering. Some put the year first, some put it last.

So, how can I make this happen?

Any ideas?


This is generic solution that works for all cultures:

var r = new Regex(@"^(?<first>[^y]*?)(?<year>y+)(?<second>[^y]*?)$");
var mc = r.Matches(info.DateTimeFormat.ShortDatePattern);
var f = mc[0].Groups["first"].Value;
var y = mc[0].Groups["year"].Value;
var s = mc[0].Groups["second"].Value;

this.First = string.IsNullOrEmpty(f) ? string.Empty : this.date.ToString(f, info);
this.Year = this.date.ToString(y);
try
{
    this.Second = string.IsNullOrEmpty(s) ? string.Empty : this.date.ToString(s, info);           
}
catch
{
    // fallback: sometimes the last char is just a '.'
    this.Second = s;
}

And then in your XAML:

<TextBlock>
    <Run Text="{Binding First, Mode=OneWay}" />
    <Run FontWeight="Bold" Text="{Binding Year, Mode=OneWay}" />
    <Run Text="{Binding Second, Mode=OneWay}" />
</TextBlock>

WPF: Highlight year part of a date


Do you need to support all possible cultures? If yes, it will be a little tricky. What I think I would do is to first format the date to a string according to the current culture. Then I would extract the "year" part from that string and remove it from the other part. Then you would have two strings, like so (swedish format):

yearString = "2011";
remainingDateString = "-09-01";

or in US format:

yearString = "2011";
remainingDateString = "09/01/";

You would also have to check if the "year" part of the string is at the end or at the beginning of the complete date string, and then format either the first or the second <Run> element accordingly. In the examples above, the swedish format would have its year part at the beginning of the string (YYYY-MM-DD), whereas in the US it would be at the end (MM/DD/YYYY). I do not know of any western cultures that would have the year part in the middle of the string.

Disclaimer: I do not know much about the non-western date formats, so I cannot say how well this approach would work in that part of the world.

EDIT:
As for the binding stuff, something like the following should work:

<TextBlock>
    <Run Text="{Binding DatePart1}"
            FontWeight="{Binding DatePart1FontWeight}"></Run>
    <Run Text="{Binding DatePart2}"
            FontWeight="{Binding DatePart2FontWeight}"></Run>
</TextBlock>


You need to use 2 texblocks, or textblock and label, because you just cant make part of the string of same control with another font style(Font with all its properties is same for textblock, see it in properties for example). Or you can use some richedit control (RichTextBox in readonly mode, just pass the formatted text to it)


You could use a switch case and then have the year-month-day arranged depending on the culture selected.

switch ( integral or string expression )
{
case constant-expression:
Statements
break
...
default :
Statements
break
}

puesdoCode example

Read countryOfOrigin
Switch(countryOfOrigin)
Cases of ‘New Zealand’ :
Retrieve(“day-month-**year**”)
Cases of ‘Singapore’, ‘Malaysia’, ... :
Retrieve(“month-**year**-day”)
Cases of ‘Australia’, ‘Great Britain’, … :
Retrieve(“day-month-**year**”)
Other Cases :
Retrieve(“**year**-month-day”)
End Switch

I've modified the puesdoCode, another solution would be to split the value into 2 elements down from 3 elements (so if I have a full value for example "John-Doe-Smith" and I want John [THE YEAR], split the array from the LEFT in REVERSE so the end result is eoD-nohJ and then split it again from the LEFT in REVERSE so the end result is nohJ then REVERSE your end result to get John). You could grab the "year" by also creating a splitting mechanism depending on where the year is (This could also be done by splitting every value with the "-" character and then get the length of the element retrieved in the split, to find the year you would be looking for the value with 4 characters (2011). Once you have found the value with 4 characters (1988) you just have to find position of where those 4 characters start in the string. Then retrieve your final result based off that integer position.

Best of luck sorry if it sounds complicated. I'm sure there are other ways but this is how I did it in SQL-T, and I'm completely sure an algorithm could be made the same way in C#.


Sadly your order of Runs and the way cultures represent their date time strings are two unrelated things and ONLY you can relate them ...

  1. By "somehow" hacking into C# native DateTime class and see how ToString(IFormatProvider) works (in code) and use similar thing for your advantage so that you "know" which portion of the culture specific string is year and which one is Day and thus arrange your Runs.

  2. By analysing the culture specific datetime representation to decide which is year and which is date portion yourself. You can use direct matching which will work for distinct day, month and year portions e.g. a date 01/03/2011 will work by direct matching with "03 Mar 2011" coz you can distinctly match 03 (day) with 03 and 2011 with 2011 (year). But something like 01/01/2001 may cause a trouble if some X culture represents it as "01/01/01", where you do not know which is which.

For this you can seee which cultures your product / application can be deployed to and relate the ordering of Day, Months and Years yourself by considering the ordering of Day, Month and Year doen by the culutres.

All the Best for your venture and if you get any better solution, please do share.

:-)


As others have said, you are going to need to construct the date yourself - however you can find out how the date is composed by using

Thread.CurrentThread.CurrentCulture.DateTimeFormat.ShortDatePattern

(or some of the other related members in DateTimeFormat)

You could then construct an appropriately formatted date in code (i.e. create TextBlock and Run objects in the right order to make the correct date format) and add them to your control (datePlaceholder.Children.Add(etc))

Or - and this feels like slightly more work than should really be necessary - construct Year, Month, Day and Separator classes as placeholders, data template them appropriately so that the Year is bold, then drop your component date parts into an ObservableCollection<DatePlaceholders> in the right order and throw the whole lot at an ItemsControl.

0

精彩评论

暂无评论...
验证码 换一张
取 消